@embrs/user-feedback 1.0.0

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,584 @@
1
+ /**
2
+ * MCP Feedback Collector - 前端應用
3
+ */
4
+
5
+ // 狀態管理
6
+ const state = {
7
+ sessionId: null,
8
+ images: [],
9
+ socket: null,
10
+ connected: false
11
+ };
12
+
13
+ // DOM 元素
14
+ const elements = {
15
+ workSummary: document.getElementById('work-summary'),
16
+ feedbackText: document.getElementById('feedback-text'),
17
+ dropZone: document.getElementById('drop-zone'),
18
+ fileInput: document.getElementById('file-input'),
19
+ imagePreview: document.getElementById('image-preview'),
20
+ quickReplies: document.getElementById('quick-replies'),
21
+ submitBtn: document.getElementById('submit-btn'),
22
+ addReplyBtn: document.getElementById('add-reply-btn'),
23
+ modal: document.getElementById('image-modal'),
24
+ modalImage: document.getElementById('modal-image'),
25
+ modalClose: document.getElementById('modal-close'),
26
+ toast: document.getElementById('toast')
27
+ };
28
+
29
+ // 初始化
30
+ function init() {
31
+ initSocket();
32
+ initDropZone();
33
+ initQuickReplies();
34
+ initSubmitButton();
35
+ initModal();
36
+ initAddReplyButton();
37
+ // 確保 DOM 完全載入後再載入罐頭語
38
+ setTimeout(() => {
39
+ loadQuickRepliesFromStorage();
40
+ }, 0);
41
+ }
42
+
43
+ // 初始化 WebSocket 連接
44
+ function initSocket() {
45
+ state.socket = io();
46
+
47
+ state.socket.on('connect', () => {
48
+ console.log('WebSocket 已連接');
49
+ state.connected = true;
50
+ state.socket.emit('request_session');
51
+ });
52
+
53
+ state.socket.on('disconnect', () => {
54
+ console.log('WebSocket 已斷開');
55
+ state.connected = false;
56
+ });
57
+
58
+ state.socket.on('session_assigned', (data) => {
59
+ console.log('會話已分配:', data);
60
+ state.sessionId = data.session_id;
61
+ displayWorkSummary(data.work_summary);
62
+ });
63
+
64
+ state.socket.on('no_active_session', (data) => {
65
+ console.log('無活躍會話:', data);
66
+ elements.workSummary.innerHTML = '<div class="loading">等待 AI 工作彙報...</div>';
67
+ });
68
+
69
+ state.socket.on('work_summary_data', (data) => {
70
+ displayWorkSummary(data.work_summary);
71
+ });
72
+
73
+ state.socket.on('feedback_submitted', (data) => {
74
+ if (data.success) {
75
+ showToast('反饋提交成功!', 'success');
76
+ resetForm();
77
+
78
+ // 延遲關閉網頁
79
+ setTimeout(() => {
80
+ window.close();
81
+ }, 1000);
82
+ }
83
+ });
84
+
85
+ state.socket.on('feedback_error', (data) => {
86
+ showToast(data.error, 'error');
87
+ });
88
+ }
89
+
90
+ // 顯示工作彙報
91
+ function displayWorkSummary(content) {
92
+ if (!content) {
93
+ elements.workSummary.innerHTML = '<div class="loading">等待 AI 工作彙報...</div>';
94
+ return;
95
+ }
96
+
97
+ // 簡單的 Markdown 轉換
98
+ let html = content
99
+ .replace(/^### (.+)$/gm, '<h3>$1</h3>')
100
+ .replace(/^## (.+)$/gm, '<h2>$1</h2>')
101
+ .replace(/^# (.+)$/gm, '<h1>$1</h1>')
102
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
103
+ .replace(/\*(.+?)\*/g, '<em>$1</em>')
104
+ .replace(/`(.+?)`/g, '<code>$1</code>')
105
+ .replace(/\n/g, '<br>');
106
+
107
+ elements.workSummary.innerHTML = html;
108
+
109
+ // 自動 focus 在輸入框
110
+ setTimeout(() => {
111
+ elements.feedbackText.focus();
112
+ }, 100);
113
+ }
114
+
115
+ // 初始化拖放區域
116
+ function initDropZone() {
117
+ const dropZone = elements.dropZone;
118
+ const fileInput = elements.fileInput;
119
+
120
+ dropZone.addEventListener('click', () => fileInput.click());
121
+
122
+ dropZone.addEventListener('dragover', (e) => {
123
+ e.preventDefault();
124
+ dropZone.classList.add('drag-over');
125
+ });
126
+
127
+ dropZone.addEventListener('dragleave', () => {
128
+ dropZone.classList.remove('drag-over');
129
+ });
130
+
131
+ dropZone.addEventListener('drop', (e) => {
132
+ e.preventDefault();
133
+ dropZone.classList.remove('drag-over');
134
+ handleFiles(e.dataTransfer.files);
135
+ });
136
+
137
+ fileInput.addEventListener('change', (e) => {
138
+ handleFiles(e.target.files);
139
+ fileInput.value = '';
140
+ });
141
+ }
142
+
143
+ // 處理檔案
144
+ function handleFiles(files) {
145
+ Array.from(files).forEach(file => {
146
+ if (!file.type.startsWith('image/')) {
147
+ showToast('只支援圖片檔案', 'error');
148
+ return;
149
+ }
150
+
151
+ const reader = new FileReader();
152
+ reader.onload = (e) => {
153
+ const imageData = {
154
+ name: file.name,
155
+ type: file.type,
156
+ size: file.size,
157
+ data: e.target.result
158
+ };
159
+ state.images.push(imageData);
160
+ renderImagePreviews();
161
+ };
162
+ reader.readAsDataURL(file);
163
+ });
164
+ }
165
+
166
+ // 渲染圖片預覽
167
+ function renderImagePreviews() {
168
+ elements.imagePreview.innerHTML = '';
169
+
170
+ state.images.forEach((img, index) => {
171
+ const item = document.createElement('div');
172
+ item.className = 'preview-item';
173
+ item.draggable = true;
174
+ item.dataset.index = index;
175
+
176
+ item.innerHTML = `
177
+ <img src="${img.data}" alt="${img.name}">
178
+ <button class="remove-btn" data-index="${index}">&times;</button>
179
+ `;
180
+
181
+ // 點擊圖片開啟彈窗
182
+ item.querySelector('img').addEventListener('click', () => openModal(img.data));
183
+
184
+ // 移除按鈕
185
+ item.querySelector('.remove-btn').addEventListener('click', (e) => {
186
+ e.stopPropagation();
187
+ removeImage(index);
188
+ });
189
+
190
+ // 拖動排序
191
+ item.addEventListener('dragstart', handleDragStart);
192
+ item.addEventListener('dragend', handleDragEnd);
193
+ item.addEventListener('dragover', handleDragOver);
194
+ item.addEventListener('drop', handleDrop);
195
+
196
+ elements.imagePreview.appendChild(item);
197
+ });
198
+ }
199
+
200
+ // 移除圖片
201
+ function removeImage(index) {
202
+ state.images.splice(index, 1);
203
+ renderImagePreviews();
204
+ }
205
+
206
+ // 拖動排序處理
207
+ let draggedIndex = null;
208
+ let draggedItem = null;
209
+
210
+ function handleDragStart(e) {
211
+ const previewItem = e.target.closest('.preview-item');
212
+ draggedIndex = parseInt(previewItem.dataset.index);
213
+ draggedItem = state.images[draggedIndex];
214
+ previewItem.classList.add('dragging');
215
+ }
216
+
217
+ function handleDragEnd(e) {
218
+ e.target.closest('.preview-item').classList.remove('dragging');
219
+ draggedIndex = null;
220
+ draggedItem = null;
221
+ }
222
+
223
+ function handleDragOver(e) {
224
+ e.preventDefault();
225
+ }
226
+
227
+ function handleDrop(e) {
228
+ e.preventDefault();
229
+ const dropTarget = e.target.closest('.preview-item');
230
+
231
+ if (!dropTarget || draggedIndex === null) return;
232
+
233
+ const dropIndex = parseInt(dropTarget.dataset.index);
234
+
235
+ if (draggedIndex !== dropIndex && draggedItem) {
236
+ // 創建新的圖片陣列
237
+ const newImages = [...state.images];
238
+
239
+ // 移除拖動的項目
240
+ newImages.splice(draggedIndex, 1);
241
+
242
+ // 重新計算插入位置(因為移除後索引可能改變)
243
+ const newDropIndex = draggedIndex < dropIndex ? dropIndex - 1 : dropIndex;
244
+
245
+ // 插入到新位置
246
+ newImages.splice(newDropIndex, 0, draggedItem);
247
+
248
+ // 更新狀態
249
+ state.images = newImages;
250
+ renderImagePreviews();
251
+ }
252
+ }
253
+
254
+ // 初始化罐頭語按鈕
255
+ function initQuickReplies() {
256
+ elements.quickReplies.addEventListener('click', (e) => {
257
+ if (e.target.classList.contains('quick-reply-btn')) {
258
+ const text = e.target.dataset.text;
259
+ // 直接提交並關閉
260
+ submitQuickReply(text);
261
+ }
262
+ });
263
+
264
+ // 雙擊編輯罐頭語
265
+ elements.quickReplies.addEventListener('dblclick', (e) => {
266
+ if (e.target.classList.contains('quick-reply-btn')) {
267
+ editQuickReply(e.target);
268
+ }
269
+ });
270
+ }
271
+
272
+ // 初始化新增罐頭語按鈕
273
+ function initAddReplyButton() {
274
+ elements.addReplyBtn.addEventListener('click', () => {
275
+ addNewQuickReply();
276
+ });
277
+ }
278
+
279
+ // 編輯罐頭語
280
+ function editQuickReply(btn) {
281
+ const originalText = btn.textContent;
282
+ const input = document.createElement('input');
283
+ input.type = 'text';
284
+ input.value = originalText;
285
+ input.className = 'quick-reply-input';
286
+ input.style.cssText = 'padding: 8px; border-radius: 16px; border: 1px solid var(--accent); background: var(--bg-secondary); color: var(--text-primary); font-size: 13px; width: 120px;';
287
+
288
+ btn.replaceWith(input);
289
+ input.focus();
290
+ input.select();
291
+
292
+ let isSaving = false; // 防止重複保存
293
+
294
+ const save = () => {
295
+ if (isSaving) return; // 防止重複執行
296
+ isSaving = true;
297
+
298
+ const newText = input.value.trim() || originalText;
299
+ const newBtn = createQuickReplyButton(newText, newText === '無' ? '' : newText);
300
+ input.replaceWith(newBtn);
301
+ saveQuickRepliesToStorage();
302
+
303
+ // 重置保存狀態
304
+ setTimeout(() => {
305
+ isSaving = false;
306
+ }, 100);
307
+ };
308
+
309
+ input.addEventListener('blur', save);
310
+ input.addEventListener('keydown', (e) => {
311
+ if (e.key === 'Enter') {
312
+ e.preventDefault(); // 防止其他事件
313
+ save();
314
+ } else if (e.key === 'Escape') {
315
+ const newBtn = createQuickReplyButton(originalText, originalText === '無' ? '' : originalText);
316
+ input.replaceWith(newBtn);
317
+ }
318
+ });
319
+ }
320
+
321
+ // 新增罐頭語
322
+ function addNewQuickReply() {
323
+ // 檢查是否已經有輸入框
324
+ if (elements.quickReplies.querySelector('.quick-reply-input')) {
325
+ return;
326
+ }
327
+
328
+ const input = document.createElement('input');
329
+ input.type = 'text';
330
+ input.placeholder = '輸入新的罐頭語';
331
+ input.className = 'quick-reply-input';
332
+ input.style.cssText = 'padding: 8px; border-radius: 16px; border: 1px solid var(--accent); background: var(--bg-secondary); color: var(--text-primary); font-size: 13px; width: 150px;';
333
+
334
+ // 在新增按鈕前插入輸入框
335
+ elements.addReplyBtn.parentNode.insertBefore(input, elements.addReplyBtn);
336
+ input.focus();
337
+
338
+ let isSaving = false; // 防止重複保存
339
+
340
+ const save = () => {
341
+ if (isSaving) return; // 防止重複執行
342
+ isSaving = true;
343
+
344
+ const newText = input.value.trim();
345
+ if (newText) {
346
+ // 檢查是否已存在相同的罐頭語
347
+ const existingBtns = elements.quickReplies.querySelectorAll('.quick-reply-btn');
348
+ const exists = Array.from(existingBtns).some(btn => btn.dataset.text === newText);
349
+
350
+ if (!exists) {
351
+ // 創建新的按鈕(會自動插入到新增按鈕之前)
352
+ createQuickReplyButton(newText, newText);
353
+
354
+ // 儲存到 localStorage
355
+ saveQuickRepliesToStorage();
356
+
357
+ showToast('罐頭語已新增', 'success');
358
+ } else {
359
+ showToast('罐頭語已存在', 'error');
360
+ }
361
+ }
362
+
363
+ // 移除輸入框
364
+ input.remove();
365
+
366
+ // 重置保存狀態
367
+ setTimeout(() => {
368
+ isSaving = false;
369
+ }, 100);
370
+ };
371
+
372
+ input.addEventListener('blur', save);
373
+ input.addEventListener('keydown', (e) => {
374
+ if (e.key === 'Enter') {
375
+ e.preventDefault(); // 防止其他事件
376
+ save();
377
+ } else if (e.key === 'Escape') {
378
+ input.remove();
379
+ }
380
+ });
381
+ }
382
+
383
+ // 儲存罐頭語到後端 API
384
+ async function saveQuickRepliesToStorage() {
385
+ const buttons = elements.quickReplies.querySelectorAll('.quick-reply-btn');
386
+ const replies = Array.from(buttons).map(btn => ({
387
+ text: btn.querySelector('span')?.textContent || btn.textContent,
388
+ value: btn.dataset.text
389
+ }));
390
+
391
+ try {
392
+ await fetch('/api/quick-replies', {
393
+ method: 'POST',
394
+ headers: { 'Content-Type': 'application/json' },
395
+ body: JSON.stringify({ replies })
396
+ });
397
+ } catch (error) {
398
+ console.error('儲存罐頭語失敗:', error);
399
+ }
400
+
401
+ // 確保新增按鈕始終可見
402
+ ensureAddButtonVisible();
403
+ }
404
+
405
+ // 確保新增按鈕可見
406
+ function ensureAddButtonVisible() {
407
+ if (!elements.quickReplies.contains(elements.addReplyBtn)) {
408
+ elements.quickReplies.appendChild(elements.addReplyBtn);
409
+ }
410
+ }
411
+
412
+ // 從後端 API 載入罐頭語
413
+ async function loadQuickRepliesFromStorage() {
414
+ try {
415
+ const response = await fetch('/api/quick-replies');
416
+ const data = await response.json();
417
+
418
+ if (data.success && Array.isArray(data.replies)) {
419
+ elements.quickReplies.innerHTML = '';
420
+ data.replies.forEach(reply => {
421
+ createQuickReplyButton(reply.text, reply.value);
422
+ });
423
+ }
424
+ } catch (error) {
425
+ console.error('載入罐頭語失敗:', error);
426
+ }
427
+
428
+ // 確保新增按鈕始終存在
429
+ ensureAddButtonVisible();
430
+ }
431
+
432
+ // 創建罐頭語按鈕
433
+ function createQuickReplyButton(text, value) {
434
+ const btn = document.createElement('button');
435
+ btn.className = 'quick-reply-btn';
436
+ btn.dataset.text = value;
437
+
438
+ // 創建文字 span
439
+ const textSpan = document.createElement('span');
440
+ textSpan.textContent = text;
441
+ textSpan.style.pointerEvents = 'none'; // 防止文字影響點擊事件
442
+
443
+ // 添加刪除按鈕
444
+ const deleteBtn = document.createElement('button');
445
+ deleteBtn.className = 'delete-btn';
446
+ deleteBtn.textContent = '×';
447
+ deleteBtn.title = '刪除罐頭語';
448
+ deleteBtn.setAttribute('aria-label', '刪除罐頭語');
449
+
450
+ // 刪除事件
451
+ deleteBtn.addEventListener('click', (e) => {
452
+ e.stopPropagation();
453
+ if (confirm(`確定要刪除罐頭語「${text}」嗎?`)) {
454
+ btn.remove();
455
+ saveQuickRepliesToStorage();
456
+ showToast('罐頭語已刪除', 'success');
457
+ }
458
+ });
459
+
460
+ // 組裝按鈕結構
461
+ btn.appendChild(textSpan);
462
+ btn.appendChild(deleteBtn);
463
+
464
+ // 安全地插入按鈕
465
+ try {
466
+ if (elements.addReplyBtn && elements.quickReplies.contains(elements.addReplyBtn)) {
467
+ elements.quickReplies.insertBefore(btn, elements.addReplyBtn);
468
+ } else {
469
+ elements.quickReplies.appendChild(btn);
470
+ }
471
+ } catch (error) {
472
+ console.error('插入罐頭語按鈕失敗:', error);
473
+ elements.quickReplies.appendChild(btn);
474
+ }
475
+
476
+ return btn;
477
+ }
478
+
479
+ // 初始化提交按鈕
480
+ function initSubmitButton() {
481
+ elements.submitBtn.addEventListener('click', submitFeedback);
482
+ }
483
+
484
+ // 快速提交罐頭語
485
+ function submitQuickReply(text) {
486
+ if (!state.sessionId) {
487
+ showToast('無活躍會話,請等待 AI 調用', 'error');
488
+ return;
489
+ }
490
+
491
+ const feedbackData = {
492
+ sessionId: state.sessionId,
493
+ text: text,
494
+ images: [],
495
+ timestamp: Date.now()
496
+ };
497
+
498
+ state.socket.emit('submit_feedback', feedbackData);
499
+ }
500
+
501
+ // 提交反饋
502
+ function submitFeedback() {
503
+ const text = elements.feedbackText.value.trim();
504
+ const images = state.images;
505
+
506
+ if (!text && images.length === 0) {
507
+ showToast('請輸入反饋或上傳圖片', 'error');
508
+ return;
509
+ }
510
+
511
+ if (!state.sessionId) {
512
+ showToast('無活躍會話,請等待 AI 調用', 'error');
513
+ return;
514
+ }
515
+
516
+ const feedbackData = {
517
+ sessionId: state.sessionId,
518
+ text: text,
519
+ images: images.map(img => ({
520
+ name: img.name,
521
+ type: img.type,
522
+ size: img.size,
523
+ data: img.data
524
+ })),
525
+ timestamp: Date.now()
526
+ };
527
+
528
+ elements.submitBtn.disabled = true;
529
+ state.socket.emit('submit_feedback', feedbackData);
530
+
531
+ setTimeout(() => {
532
+ elements.submitBtn.disabled = false;
533
+ }, 2000);
534
+ }
535
+
536
+ // 重置表單
537
+ function resetForm() {
538
+ elements.feedbackText.value = '';
539
+ state.images = [];
540
+ renderImagePreviews();
541
+ state.sessionId = null;
542
+ elements.workSummary.innerHTML = '<div class="loading">等待 AI 工作彙報...</div>';
543
+ }
544
+
545
+ // 初始化圖片彈窗
546
+ function initModal() {
547
+ elements.modalClose.addEventListener('click', closeModal);
548
+ elements.modal.addEventListener('click', (e) => {
549
+ if (e.target === elements.modal) {
550
+ closeModal();
551
+ }
552
+ });
553
+
554
+ document.addEventListener('keydown', (e) => {
555
+ if (e.key === 'Escape' && elements.modal.classList.contains('show')) {
556
+ closeModal();
557
+ }
558
+ });
559
+ }
560
+
561
+ // 開啟彈窗
562
+ function openModal(imageSrc) {
563
+ elements.modalImage.src = imageSrc;
564
+ elements.modal.classList.add('show');
565
+ }
566
+
567
+ // 關閉彈窗
568
+ function closeModal() {
569
+ elements.modal.classList.remove('show');
570
+ elements.modalImage.src = '';
571
+ }
572
+
573
+ // 顯示 Toast 提示
574
+ function showToast(message, type = 'info') {
575
+ elements.toast.textContent = message;
576
+ elements.toast.className = 'toast show ' + type;
577
+
578
+ setTimeout(() => {
579
+ elements.toast.classList.remove('show');
580
+ }, 3000);
581
+ }
582
+
583
+ // 頁面載入完成後初始化
584
+ document.addEventListener('DOMContentLoaded', init);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/static/app.js"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-TW">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>MCP Feedback Collector</title>
7
+ <link rel="stylesheet" href="styles.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <!-- 左側:AI 工作彙報 -->
12
+ <div class="panel panel-left">
13
+ <div class="panel-header">
14
+ <h2>AI 工作彙報</h2>
15
+ </div>
16
+ <div class="panel-content" id="work-summary">
17
+ <div class="loading">等待 AI 工作彙報...</div>
18
+ </div>
19
+ </div>
20
+
21
+ <!-- 右側:用戶反饋 -->
22
+ <div class="panel panel-right">
23
+ <div class="panel-header">
24
+ <h2>用戶反饋</h2>
25
+ </div>
26
+ <div class="panel-content">
27
+ <!-- 圖片上傳區域 -->
28
+ <div class="image-section">
29
+ <div class="drop-zone" id="drop-zone">
30
+ <p>拖放圖片到此處或點擊上傳</p>
31
+ <input type="file" id="file-input" accept="image/*" multiple hidden>
32
+ </div>
33
+ <div class="image-preview" id="image-preview"></div>
34
+ </div>
35
+
36
+ <!-- 文字輸入區域 -->
37
+ <div class="text-section">
38
+ <textarea
39
+ id="feedback-text"
40
+ placeholder="輸入您的反饋..."
41
+ rows="6"
42
+ ></textarea>
43
+ </div>
44
+
45
+ <!-- 罐頭語按鈕 -->
46
+ <div class="quick-replies" id="quick-replies">
47
+ <button class="quick-reply-btn" data-text="">無</button>
48
+ <button class="quick-reply-btn" data-text="請繼續">請繼續</button>
49
+ <button class="quick-reply-btn" data-text="請使用你覺得的最佳解">請使用你覺得的最佳解</button>
50
+ <button class="add-reply-btn" id="add-reply-btn" title="新增罐頭語">+</button>
51
+ </div>
52
+
53
+ <!-- 提交按鈕 -->
54
+ <div class="submit-section">
55
+ <button id="submit-btn" class="submit-btn">提交反饋</button>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- 圖片彈窗 -->
62
+ <div class="modal" id="image-modal">
63
+ <div class="modal-content">
64
+ <span class="modal-close" id="modal-close">&times;</span>
65
+ <img id="modal-image" src="" alt="Preview">
66
+ </div>
67
+ </div>
68
+
69
+ <!-- 狀態提示 -->
70
+ <div class="toast" id="toast"></div>
71
+
72
+ <script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
73
+ <script src="app.js"></script>
74
+ </body>
75
+ </html>