@hirohsu/user-web-feedback 2.6.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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +953 -0
  3. package/dist/cli.cjs +95778 -0
  4. package/dist/index.cjs +92818 -0
  5. package/dist/static/app.js +385 -0
  6. package/dist/static/components/navbar.css +406 -0
  7. package/dist/static/components/navbar.html +49 -0
  8. package/dist/static/components/navbar.js +211 -0
  9. package/dist/static/dashboard.css +495 -0
  10. package/dist/static/dashboard.html +95 -0
  11. package/dist/static/dashboard.js +540 -0
  12. package/dist/static/favicon.svg +27 -0
  13. package/dist/static/index.html +541 -0
  14. package/dist/static/logs.html +376 -0
  15. package/dist/static/logs.js +442 -0
  16. package/dist/static/mcp-settings.html +797 -0
  17. package/dist/static/mcp-settings.js +884 -0
  18. package/dist/static/modules/app-core.js +124 -0
  19. package/dist/static/modules/conversation-panel.js +247 -0
  20. package/dist/static/modules/feedback-handler.js +1420 -0
  21. package/dist/static/modules/image-handler.js +155 -0
  22. package/dist/static/modules/log-viewer.js +296 -0
  23. package/dist/static/modules/mcp-manager.js +474 -0
  24. package/dist/static/modules/prompt-manager.js +364 -0
  25. package/dist/static/modules/settings-manager.js +299 -0
  26. package/dist/static/modules/socket-manager.js +170 -0
  27. package/dist/static/modules/state-manager.js +352 -0
  28. package/dist/static/modules/timer-controller.js +243 -0
  29. package/dist/static/modules/ui-helpers.js +246 -0
  30. package/dist/static/settings.html +355 -0
  31. package/dist/static/settings.js +425 -0
  32. package/dist/static/socket.io.min.js +7 -0
  33. package/dist/static/style.css +2157 -0
  34. package/dist/static/terminals.html +357 -0
  35. package/dist/static/terminals.js +321 -0
  36. package/package.json +91 -0
@@ -0,0 +1,385 @@
1
+ /**
2
+ * user-feedback MCP Tools - Enhanced UI
3
+ * 前端 JavaScript 主檔案 (ES6 Modules Version)
4
+ */
5
+
6
+ import { initSocketIO } from "./modules/socket-manager.js";
7
+
8
+ import {
9
+ loadInitialData,
10
+ handleUserActivity,
11
+ updateCharCount,
12
+ submitFeedback,
13
+ clearImages,
14
+ handleFileSelect,
15
+ handleFileDrop,
16
+ handlePaste,
17
+ removeImage,
18
+ } from "./modules/app-core.js";
19
+
20
+ import {
21
+ renderPrompts,
22
+ filterPrompts,
23
+ usePrompt,
24
+ togglePinPrompt,
25
+ editPrompt,
26
+ deletePrompt,
27
+ openPromptModal,
28
+ closePromptModal,
29
+ savePrompt,
30
+ } from "./modules/prompt-manager.js";
31
+
32
+ import {
33
+ generateAIReplyWithTools,
34
+ cancelAutoReplyConfirm,
35
+ confirmAutoReplySubmit,
36
+ } from "./modules/feedback-handler.js";
37
+
38
+ import {
39
+ loadMCPServers,
40
+ openMCPServersModal,
41
+ closeMCPServersModal,
42
+ openMCPServerEditModal,
43
+ closeMCPServerEditModal,
44
+ onTransportChange,
45
+ saveMCPServer,
46
+ connectAllMCPServers,
47
+ disconnectAllMCPServers,
48
+ connectMCPServer,
49
+ disconnectMCPServer,
50
+ editMCPServer,
51
+ deleteMCPServerConfirm,
52
+ } from "./modules/mcp-manager.js";
53
+
54
+ import {
55
+ openAISettingsModal,
56
+ closeAISettingsModal,
57
+ saveAISettings,
58
+ testAPIKey,
59
+ toggleAPIKeyVisibility,
60
+ } from "./modules/settings-manager.js";
61
+
62
+ import {
63
+ openLogViewerModal,
64
+ closeLogViewerModal,
65
+ loadLogs,
66
+ searchLogs,
67
+ clearOldLogs,
68
+ handlePagination,
69
+ } from "./modules/log-viewer.js";
70
+
71
+ import {
72
+ showToast,
73
+ showAlertModal,
74
+ hideAlertModal,
75
+ } from "./modules/ui-helpers.js";
76
+
77
+ import {
78
+ resumeAutoReplyTimer,
79
+ pauseAutoReplyTimer,
80
+ } from "./modules/timer-controller.js";
81
+
82
+ import { isAutoReplyTimerPaused } from "./modules/state-manager.js";
83
+
84
+ // ============ 初始化 ============
85
+
86
+ document.addEventListener("DOMContentLoaded", () => {
87
+ console.log("Enhanced UI 初始化 (ES6 Modules)...");
88
+
89
+ // 初始化 Socket.IO
90
+ initSocketIO();
91
+
92
+ // 初始化事件監聽器
93
+ initEventListeners();
94
+
95
+ // 載入資料
96
+ loadInitialData();
97
+ });
98
+
99
+ // ============ 事件監聽器 ============
100
+
101
+ function initEventListeners() {
102
+ // 文字輸入區
103
+ const feedbackText = document.getElementById("feedbackText");
104
+
105
+ // 設定初始前綴
106
+ feedbackText.value = "以下為我的回覆:\n";
107
+ updateCharCount();
108
+
109
+ feedbackText.addEventListener("input", handleUserActivity);
110
+ feedbackText.addEventListener("input", updateCharCount);
111
+
112
+ // 當使用者將焦點放在文字框時,暫停自動回覆倒數
113
+ feedbackText.addEventListener("focus", () => {
114
+ // 只有在計時器正在運作且尚未暫停的情況下執行
115
+ // 注意:這裡需要檢查計時器狀態,但由於狀態分散在各模組,
116
+ // 我們依賴 pauseAutoReplyTimer 內部的檢查邏輯
117
+ pauseAutoReplyTimer(true);
118
+ });
119
+
120
+ // Ctrl+Enter 提交
121
+ feedbackText.addEventListener("keydown", (e) => {
122
+ if (e.ctrlKey && e.key === "Enter") {
123
+ e.preventDefault();
124
+ submitFeedback();
125
+ }
126
+ });
127
+
128
+ // 提交按鈕
129
+ document
130
+ .getElementById("submitBtn")
131
+ .addEventListener("click", submitFeedback);
132
+
133
+ // AI 回覆按鈕 - 使用支援 MCP 工具的版本
134
+ document
135
+ .getElementById("aiReplyBtn")
136
+ .addEventListener("click", generateAIReplyWithTools);
137
+
138
+ // 圖片區域
139
+ const imageDropZone = document.getElementById("imageDropZone");
140
+ const fileInput = document.getElementById("fileInput");
141
+
142
+ imageDropZone.addEventListener("click", () => fileInput.click());
143
+ fileInput.addEventListener("change", handleFileSelect);
144
+
145
+ // 拖放事件
146
+ imageDropZone.addEventListener("dragover", (e) => {
147
+ e.preventDefault();
148
+ imageDropZone.classList.add("drag-over");
149
+ });
150
+
151
+ imageDropZone.addEventListener("dragleave", () => {
152
+ imageDropZone.classList.remove("drag-over");
153
+ });
154
+
155
+ imageDropZone.addEventListener("drop", (e) => {
156
+ e.preventDefault();
157
+ imageDropZone.classList.remove("drag-over");
158
+ handleFileDrop(e.dataTransfer.files);
159
+ });
160
+
161
+ // 貼上事件
162
+ document.addEventListener("paste", handlePaste);
163
+
164
+ // 清除圖片按鈕
165
+ document
166
+ .getElementById("clearImagesBtn")
167
+ .addEventListener("click", clearImages);
168
+
169
+ // 提示詞區域
170
+ document
171
+ .getElementById("promptSearch")
172
+ .addEventListener("input", filterPrompts);
173
+ document
174
+ .getElementById("addPromptBtn")
175
+ .addEventListener("click", () => openPromptModal());
176
+ document
177
+ .getElementById("addPromptBtnFooter")
178
+ .addEventListener("click", () => openPromptModal());
179
+
180
+ // AI 設定中的編輯提示詞按鈕
181
+ const editPromptsFromSettings = document.getElementById(
182
+ "editPromptsFromSettings"
183
+ );
184
+ if (editPromptsFromSettings) {
185
+ editPromptsFromSettings.addEventListener("click", () => {
186
+ openPromptModal();
187
+ });
188
+ }
189
+
190
+ // AI 設定按鈕(可能已移至導覽欄)
191
+ const aiSettingsBtn = document.getElementById("aiSettingsBtn");
192
+ if (aiSettingsBtn) {
193
+ aiSettingsBtn.addEventListener("click", openAISettingsModal);
194
+ }
195
+
196
+ // MCP Servers 按鈕(可能已移至導覽欄)
197
+ const mcpServersBtn = document.getElementById("mcpServersBtn");
198
+ if (mcpServersBtn) {
199
+ mcpServersBtn.addEventListener("click", openMCPServersModal);
200
+ }
201
+ const closeMcpServers = document.getElementById("closeMcpServers");
202
+ if (closeMcpServers) {
203
+ closeMcpServers.addEventListener("click", closeMCPServersModal);
204
+ }
205
+ const addMcpServer = document.getElementById("addMcpServer");
206
+ if (addMcpServer) {
207
+ addMcpServer.addEventListener("click", () => openMCPServerEditModal());
208
+ }
209
+ const closeMcpServerEdit = document.getElementById("closeMcpServerEdit");
210
+ if (closeMcpServerEdit) {
211
+ closeMcpServerEdit.addEventListener("click", closeMCPServerEditModal);
212
+ }
213
+ const cancelMcpServer = document.getElementById("cancelMcpServer");
214
+ if (cancelMcpServer) {
215
+ cancelMcpServer.addEventListener("click", closeMCPServerEditModal);
216
+ }
217
+ const saveMcpServerBtn = document.getElementById("saveMcpServer");
218
+ if (saveMcpServerBtn) {
219
+ saveMcpServerBtn.addEventListener("click", saveMCPServer);
220
+ }
221
+ const connectAllBtn = document.getElementById("connectAllMcpServers");
222
+ if (connectAllBtn) {
223
+ connectAllBtn.addEventListener("click", connectAllMCPServers);
224
+ }
225
+ const disconnectAllBtn = document.getElementById("disconnectAllMcpServers");
226
+ if (disconnectAllBtn) {
227
+ disconnectAllBtn.addEventListener("click", disconnectAllMCPServers);
228
+ }
229
+ const mcpServerTransport = document.getElementById("mcpServerTransport");
230
+ if (mcpServerTransport) {
231
+ mcpServerTransport.addEventListener("change", onTransportChange);
232
+ }
233
+
234
+ // 彈窗控制
235
+ const closeAiSettingsBtn = document.getElementById("closeAiSettings");
236
+ if (closeAiSettingsBtn) {
237
+ closeAiSettingsBtn.addEventListener("click", closeAISettingsModal);
238
+ }
239
+ const saveAiSettingsBtn = document.getElementById("saveAiSettings");
240
+ if (saveAiSettingsBtn) {
241
+ saveAiSettingsBtn.addEventListener("click", saveAISettings);
242
+ }
243
+ const testApiKeyBtn = document.getElementById("testApiKey");
244
+ if (testApiKeyBtn) {
245
+ testApiKeyBtn.addEventListener("click", testAPIKey);
246
+ }
247
+ const toggleApiKeyBtn = document.getElementById("toggleApiKey");
248
+ if (toggleApiKeyBtn) {
249
+ toggleApiKeyBtn.addEventListener("click", toggleAPIKeyVisibility);
250
+ }
251
+
252
+ // 通用提醒彈窗確定按鈕
253
+ const alertOkBtn = document.getElementById("alertModalOk");
254
+ if (alertOkBtn) {
255
+ alertOkBtn.addEventListener("click", hideAlertModal);
256
+ }
257
+
258
+ document
259
+ .getElementById("closePromptModal")
260
+ .addEventListener("click", closePromptModal);
261
+ document
262
+ .getElementById("cancelPrompt")
263
+ .addEventListener("click", closePromptModal);
264
+ document.getElementById("savePrompt").addEventListener("click", savePrompt);
265
+
266
+ // 日誌檢視器按鈕(可能已移至導覽欄)
267
+ const logViewerBtn = document.getElementById("logViewerBtn");
268
+ if (logViewerBtn) {
269
+ logViewerBtn.addEventListener("click", openLogViewerModal);
270
+ }
271
+ const closeLogViewer = document.getElementById("closeLogViewer");
272
+ if (closeLogViewer) {
273
+ closeLogViewer.addEventListener("click", closeLogViewerModal);
274
+ }
275
+ const logSearchBtn = document.getElementById("logSearchBtn");
276
+ if (logSearchBtn) {
277
+ logSearchBtn.addEventListener("click", searchLogs);
278
+ }
279
+ const logRefreshBtn = document.getElementById("logRefreshBtn");
280
+ if (logRefreshBtn) {
281
+ logRefreshBtn.addEventListener("click", () => loadLogs(1));
282
+ }
283
+ const logPrevPage = document.getElementById("logPrevPage");
284
+ if (logPrevPage) {
285
+ logPrevPage.addEventListener("click", () => handlePagination("prev"));
286
+ }
287
+ const logNextPage = document.getElementById("logNextPage");
288
+ if (logNextPage) {
289
+ logNextPage.addEventListener("click", () => handlePagination("next"));
290
+ }
291
+ const clearOldLogsBtn = document.getElementById("clearOldLogs");
292
+ if (clearOldLogsBtn) {
293
+ clearOldLogsBtn.addEventListener("click", clearOldLogs);
294
+ }
295
+ const logSearch = document.getElementById("logSearch");
296
+ if (logSearch) {
297
+ logSearch.addEventListener("keydown", (e) => {
298
+ if (e.key === "Enter") {
299
+ e.preventDefault();
300
+ searchLogs();
301
+ }
302
+ });
303
+ }
304
+
305
+ // 點擊自動回覆計時區塊可切換暫停/繼續
306
+ const setupTimerClick = (element) => {
307
+ if (!element) return;
308
+ element.style.cursor = "pointer";
309
+ element.addEventListener("click", (e) => {
310
+ e.stopPropagation();
311
+ if (isAutoReplyTimerPaused()) {
312
+ resumeAutoReplyTimer();
313
+ showToast("info", "計時器已繼續", "自動回覆倒數已恢復。");
314
+ } else {
315
+ pauseAutoReplyTimer(false);
316
+ showToast(
317
+ "info",
318
+ "計時器已暫停",
319
+ "已手動暫停自動回覆倒數,點擊可繼續。"
320
+ );
321
+ }
322
+ });
323
+ };
324
+
325
+ // 設置底部計時器點擊事件
326
+ setupTimerClick(document.getElementById("auto-reply-timer-bottom"));
327
+
328
+ // 自動回覆確認模態框
329
+ const closeAutoReplyConfirmBtn = document.getElementById(
330
+ "closeAutoReplyConfirm"
331
+ );
332
+ if (closeAutoReplyConfirmBtn) {
333
+ closeAutoReplyConfirmBtn.addEventListener("click", cancelAutoReplyConfirm);
334
+ }
335
+
336
+ const cancelAutoReplyConfirmBtn = document.getElementById(
337
+ "cancelAutoReplyConfirm"
338
+ );
339
+ if (cancelAutoReplyConfirmBtn) {
340
+ cancelAutoReplyConfirmBtn.addEventListener("click", cancelAutoReplyConfirm);
341
+ }
342
+
343
+ const confirmAutoReplySubmitBtn = document.getElementById(
344
+ "confirmAutoReplySubmit"
345
+ );
346
+ if (confirmAutoReplySubmitBtn) {
347
+ confirmAutoReplySubmitBtn.addEventListener("click", confirmAutoReplySubmit);
348
+ }
349
+
350
+ // Escape 鍵關閉自動回覆確認模態框
351
+ document.addEventListener("keydown", (e) => {
352
+ if (e.key === "Escape") {
353
+ const autoReplyModal = document.getElementById("autoReplyConfirmModal");
354
+ if (autoReplyModal && autoReplyModal.style.display === "flex") {
355
+ cancelAutoReplyConfirm();
356
+ }
357
+ }
358
+ });
359
+
360
+ // 點擊彈窗覆蓋層關閉
361
+ document.querySelectorAll(".modal-overlay").forEach((overlay) => {
362
+ overlay.addEventListener("click", (e) => {
363
+ if (e.target === overlay) {
364
+ overlay.parentElement.classList.remove("show");
365
+ }
366
+ });
367
+ });
368
+ }
369
+
370
+ // ============ 全局函數(供 HTML 調用) ============
371
+ // 由於 ES6 模組有自己的作用域,需要將這些函數掛載到 window 對象
372
+ // 這樣 HTML 中的 onclick 屬性才能訪問到它們
373
+
374
+ window.removeImage = removeImage;
375
+ window.usePrompt = usePrompt;
376
+ window.togglePinPrompt = togglePinPrompt;
377
+ window.editPrompt = editPrompt;
378
+ window.deletePrompt = deletePrompt;
379
+ window.openPromptModal = openPromptModal;
380
+
381
+ // MCP Servers 全局函數
382
+ window.connectMCPServer = connectMCPServer;
383
+ window.disconnectMCPServer = disconnectMCPServer;
384
+ window.editMCPServer = editMCPServer;
385
+ window.deleteMCPServerConfirm = deleteMCPServerConfirm;