@hirohsu/user-web-feedback 2.8.8 → 2.8.10
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/README.md +953 -953
- package/dist/cli.cjs +35102 -22236
- package/dist/index.cjs +39858 -26990
- package/dist/static/app.js +385 -385
- package/dist/static/components/navbar.css +406 -406
- package/dist/static/components/navbar.html +49 -49
- package/dist/static/components/navbar.js +211 -211
- package/dist/static/dashboard.css +495 -495
- package/dist/static/dashboard.html +95 -95
- package/dist/static/dashboard.js +540 -540
- package/dist/static/favicon.svg +27 -27
- package/dist/static/index.html +541 -541
- package/dist/static/logs.html +376 -376
- package/dist/static/logs.js +442 -442
- package/dist/static/mcp-settings.html +797 -797
- package/dist/static/mcp-settings.js +884 -884
- package/dist/static/modules/app-core.js +124 -124
- package/dist/static/modules/conversation-panel.js +247 -247
- package/dist/static/modules/feedback-handler.js +1420 -1420
- package/dist/static/modules/image-handler.js +155 -155
- package/dist/static/modules/log-viewer.js +296 -296
- package/dist/static/modules/mcp-manager.js +474 -474
- package/dist/static/modules/prompt-manager.js +364 -364
- package/dist/static/modules/settings-manager.js +299 -299
- package/dist/static/modules/socket-manager.js +170 -170
- package/dist/static/modules/state-manager.js +352 -352
- package/dist/static/modules/timer-controller.js +243 -243
- package/dist/static/modules/ui-helpers.js +246 -246
- package/dist/static/settings.html +426 -426
- package/dist/static/settings.js +767 -767
- package/dist/static/style.css +2156 -2156
- package/dist/static/terminals.html +357 -357
- package/dist/static/terminals.js +321 -321
- package/package.json +95 -95
|
@@ -1,246 +1,246 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ui-helpers.js
|
|
3
|
-
* UI 輔助函數模組
|
|
4
|
-
* 包含 Toast、Modal、Loading、HTML 轉義等工具函數
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* HTML 轉義
|
|
9
|
-
* @param {string} text - 要轉義的文字
|
|
10
|
-
* @returns {string} - 轉義後的文字
|
|
11
|
-
*/
|
|
12
|
-
export function escapeHtml(text) {
|
|
13
|
-
if (!text) return "";
|
|
14
|
-
const div = document.createElement("div");
|
|
15
|
-
div.textContent = text;
|
|
16
|
-
return div.innerHTML;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 正則表達式轉義
|
|
21
|
-
* @param {string} str - 要轉義的字串
|
|
22
|
-
* @returns {string} - 轉義後的字串
|
|
23
|
-
*/
|
|
24
|
-
export function escapeRegex(str) {
|
|
25
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* 顯示 Toast 通知
|
|
30
|
-
* @param {string} type - 類型: success, error, info
|
|
31
|
-
* @param {string} title - 標題
|
|
32
|
-
* @param {string} message - 訊息
|
|
33
|
-
*/
|
|
34
|
-
export function showToast(type, title, message) {
|
|
35
|
-
const container = document.getElementById("toastContainer");
|
|
36
|
-
if (!container) return;
|
|
37
|
-
|
|
38
|
-
const toast = document.createElement("div");
|
|
39
|
-
toast.className = `toast ${type}`;
|
|
40
|
-
toast.innerHTML = `
|
|
41
|
-
<div class="toast-icon">${getToastIcon(type)}</div>
|
|
42
|
-
<div class="toast-content">
|
|
43
|
-
<div class="toast-title">${escapeHtml(title)}</div>
|
|
44
|
-
<div class="toast-message">${escapeHtml(message)}</div>
|
|
45
|
-
</div>
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
|
-
container.appendChild(toast);
|
|
49
|
-
|
|
50
|
-
setTimeout(() => {
|
|
51
|
-
toast.style.opacity = "0";
|
|
52
|
-
setTimeout(() => toast.remove(), 300);
|
|
53
|
-
}, 3000);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* 取得 Toast 圖標
|
|
58
|
-
* @param {string} type - 類型
|
|
59
|
-
* @returns {string} - 圖標
|
|
60
|
-
*/
|
|
61
|
-
function getToastIcon(type) {
|
|
62
|
-
const icons = {
|
|
63
|
-
success: "✅",
|
|
64
|
-
error: "❌",
|
|
65
|
-
info: "ℹ️",
|
|
66
|
-
};
|
|
67
|
-
return icons[type] || "📢";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* 格式化 API 錯誤為字串
|
|
72
|
-
* @param {Object|string} data - 錯誤資料
|
|
73
|
-
* @returns {string} - 格式化後的錯誤訊息
|
|
74
|
-
*/
|
|
75
|
-
export function formatApiError(data) {
|
|
76
|
-
if (!data) return "未知錯誤";
|
|
77
|
-
if (typeof data === "string") return data;
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const parts = [];
|
|
81
|
-
if (data.error) parts.push(data.error);
|
|
82
|
-
if (data.details) {
|
|
83
|
-
parts.push(
|
|
84
|
-
typeof data.details === "string"
|
|
85
|
-
? data.details
|
|
86
|
-
: JSON.stringify(data.details)
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (data.stack) parts.push(data.stack);
|
|
90
|
-
return parts.join("\n") || JSON.stringify(data);
|
|
91
|
-
} catch (e) {
|
|
92
|
-
return String(data);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* 顯示提醒彈窗
|
|
98
|
-
* @param {string} title - 標題
|
|
99
|
-
* @param {string} message - 訊息
|
|
100
|
-
* @param {Function} onConfirm - 確認回調
|
|
101
|
-
* @param {Function} onCancel - 取消回調
|
|
102
|
-
*/
|
|
103
|
-
export function showAlertModal(
|
|
104
|
-
title,
|
|
105
|
-
message,
|
|
106
|
-
onConfirm = null,
|
|
107
|
-
onCancel = null
|
|
108
|
-
) {
|
|
109
|
-
const modal = document.getElementById("alertModal");
|
|
110
|
-
if (!modal) return;
|
|
111
|
-
|
|
112
|
-
const titleEl = document.getElementById("alertModalTitle");
|
|
113
|
-
const bodyEl = document.getElementById("alertModalBody");
|
|
114
|
-
const confirmBtn = document.getElementById("alertModalConfirm");
|
|
115
|
-
const cancelBtn = document.getElementById("alertModalCancel");
|
|
116
|
-
|
|
117
|
-
if (titleEl) titleEl.textContent = title;
|
|
118
|
-
if (bodyEl) bodyEl.textContent = message;
|
|
119
|
-
|
|
120
|
-
// 設置確認按鈕
|
|
121
|
-
if (confirmBtn) {
|
|
122
|
-
confirmBtn.onclick = () => {
|
|
123
|
-
hideAlertModal();
|
|
124
|
-
if (onConfirm) onConfirm();
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 設置取消按鈕
|
|
129
|
-
if (cancelBtn) {
|
|
130
|
-
if (onCancel) {
|
|
131
|
-
cancelBtn.style.display = "block";
|
|
132
|
-
cancelBtn.onclick = () => {
|
|
133
|
-
hideAlertModal();
|
|
134
|
-
onCancel();
|
|
135
|
-
};
|
|
136
|
-
} else {
|
|
137
|
-
cancelBtn.style.display = "none";
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
modal.classList.add("show");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* 隱藏提醒彈窗
|
|
146
|
-
*/
|
|
147
|
-
export function hideAlertModal() {
|
|
148
|
-
const modal = document.getElementById("alertModal");
|
|
149
|
-
if (modal) {
|
|
150
|
-
modal.classList.remove("show");
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 顯示載入遮罩
|
|
156
|
-
* @param {string} text - 載入文字
|
|
157
|
-
*/
|
|
158
|
-
export function showLoadingOverlay(text = "處理中...") {
|
|
159
|
-
const overlay = document.getElementById("loadingOverlay");
|
|
160
|
-
const loadingText = document.getElementById("loadingText");
|
|
161
|
-
|
|
162
|
-
if (loadingText) loadingText.textContent = text;
|
|
163
|
-
if (overlay) overlay.style.display = "flex";
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* 隱藏載入遮罩
|
|
168
|
-
*/
|
|
169
|
-
export function hideLoadingOverlay() {
|
|
170
|
-
const overlay = document.getElementById("loadingOverlay");
|
|
171
|
-
if (overlay) overlay.style.display = "none";
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 顯示專案資訊
|
|
176
|
-
* @param {string} projectName - 專案名稱
|
|
177
|
-
* @param {string} projectPath - 專案路徑
|
|
178
|
-
*/
|
|
179
|
-
export function displayProjectInfo(projectName, projectPath) {
|
|
180
|
-
const projectInfoEl = document.getElementById("projectInfo");
|
|
181
|
-
if (!projectInfoEl) return;
|
|
182
|
-
|
|
183
|
-
if (projectName || projectPath) {
|
|
184
|
-
const name = projectName || "未命名專案";
|
|
185
|
-
const path = projectPath ? ` (${projectPath})` : "";
|
|
186
|
-
projectInfoEl.innerHTML = `<span class="icon">📁</span> ${name}${path}`;
|
|
187
|
-
projectInfoEl.title = projectPath || projectName || "";
|
|
188
|
-
} else {
|
|
189
|
-
projectInfoEl.innerHTML = "";
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* 顯示 AI 訊息
|
|
195
|
-
* @param {string} message - 訊息內容 (Markdown)
|
|
196
|
-
*/
|
|
197
|
-
export function displayAIMessage(message) {
|
|
198
|
-
const displayEl = document.getElementById("aiMessageDisplay");
|
|
199
|
-
if (!displayEl) return;
|
|
200
|
-
|
|
201
|
-
const htmlContent = marked.parse(message);
|
|
202
|
-
displayEl.innerHTML = `<div class="ai-message-content">${htmlContent}</div>`;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* 更新字元計數
|
|
207
|
-
*/
|
|
208
|
-
export function updateCharCount() {
|
|
209
|
-
const textEl = document.getElementById("feedbackText");
|
|
210
|
-
const countEl = document.getElementById("charCount");
|
|
211
|
-
|
|
212
|
-
if (textEl && countEl) {
|
|
213
|
-
countEl.textContent = `${textEl.value.length} 字元`;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* 截斷過長的文字
|
|
219
|
-
* @param {string|Object} text - 要截斷的文字
|
|
220
|
-
* @param {number} maxLength - 最大長度
|
|
221
|
-
* @returns {string} - 截斷後的文字
|
|
222
|
-
*/
|
|
223
|
-
export function truncateResult(text, maxLength = 500) {
|
|
224
|
-
if (typeof text !== "string") {
|
|
225
|
-
text = JSON.stringify(text, null, 2);
|
|
226
|
-
}
|
|
227
|
-
if (text.length > maxLength) {
|
|
228
|
-
return text.substring(0, maxLength) + "\n... (已截斷)";
|
|
229
|
-
}
|
|
230
|
-
return text;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export default {
|
|
234
|
-
escapeHtml,
|
|
235
|
-
escapeRegex,
|
|
236
|
-
showToast,
|
|
237
|
-
formatApiError,
|
|
238
|
-
showAlertModal,
|
|
239
|
-
hideAlertModal,
|
|
240
|
-
showLoadingOverlay,
|
|
241
|
-
hideLoadingOverlay,
|
|
242
|
-
displayProjectInfo,
|
|
243
|
-
displayAIMessage,
|
|
244
|
-
updateCharCount,
|
|
245
|
-
truncateResult,
|
|
246
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* ui-helpers.js
|
|
3
|
+
* UI 輔助函數模組
|
|
4
|
+
* 包含 Toast、Modal、Loading、HTML 轉義等工具函數
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* HTML 轉義
|
|
9
|
+
* @param {string} text - 要轉義的文字
|
|
10
|
+
* @returns {string} - 轉義後的文字
|
|
11
|
+
*/
|
|
12
|
+
export function escapeHtml(text) {
|
|
13
|
+
if (!text) return "";
|
|
14
|
+
const div = document.createElement("div");
|
|
15
|
+
div.textContent = text;
|
|
16
|
+
return div.innerHTML;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 正則表達式轉義
|
|
21
|
+
* @param {string} str - 要轉義的字串
|
|
22
|
+
* @returns {string} - 轉義後的字串
|
|
23
|
+
*/
|
|
24
|
+
export function escapeRegex(str) {
|
|
25
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 顯示 Toast 通知
|
|
30
|
+
* @param {string} type - 類型: success, error, info
|
|
31
|
+
* @param {string} title - 標題
|
|
32
|
+
* @param {string} message - 訊息
|
|
33
|
+
*/
|
|
34
|
+
export function showToast(type, title, message) {
|
|
35
|
+
const container = document.getElementById("toastContainer");
|
|
36
|
+
if (!container) return;
|
|
37
|
+
|
|
38
|
+
const toast = document.createElement("div");
|
|
39
|
+
toast.className = `toast ${type}`;
|
|
40
|
+
toast.innerHTML = `
|
|
41
|
+
<div class="toast-icon">${getToastIcon(type)}</div>
|
|
42
|
+
<div class="toast-content">
|
|
43
|
+
<div class="toast-title">${escapeHtml(title)}</div>
|
|
44
|
+
<div class="toast-message">${escapeHtml(message)}</div>
|
|
45
|
+
</div>
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
container.appendChild(toast);
|
|
49
|
+
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
toast.style.opacity = "0";
|
|
52
|
+
setTimeout(() => toast.remove(), 300);
|
|
53
|
+
}, 3000);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 取得 Toast 圖標
|
|
58
|
+
* @param {string} type - 類型
|
|
59
|
+
* @returns {string} - 圖標
|
|
60
|
+
*/
|
|
61
|
+
function getToastIcon(type) {
|
|
62
|
+
const icons = {
|
|
63
|
+
success: "✅",
|
|
64
|
+
error: "❌",
|
|
65
|
+
info: "ℹ️",
|
|
66
|
+
};
|
|
67
|
+
return icons[type] || "📢";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 格式化 API 錯誤為字串
|
|
72
|
+
* @param {Object|string} data - 錯誤資料
|
|
73
|
+
* @returns {string} - 格式化後的錯誤訊息
|
|
74
|
+
*/
|
|
75
|
+
export function formatApiError(data) {
|
|
76
|
+
if (!data) return "未知錯誤";
|
|
77
|
+
if (typeof data === "string") return data;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const parts = [];
|
|
81
|
+
if (data.error) parts.push(data.error);
|
|
82
|
+
if (data.details) {
|
|
83
|
+
parts.push(
|
|
84
|
+
typeof data.details === "string"
|
|
85
|
+
? data.details
|
|
86
|
+
: JSON.stringify(data.details)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (data.stack) parts.push(data.stack);
|
|
90
|
+
return parts.join("\n") || JSON.stringify(data);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return String(data);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 顯示提醒彈窗
|
|
98
|
+
* @param {string} title - 標題
|
|
99
|
+
* @param {string} message - 訊息
|
|
100
|
+
* @param {Function} onConfirm - 確認回調
|
|
101
|
+
* @param {Function} onCancel - 取消回調
|
|
102
|
+
*/
|
|
103
|
+
export function showAlertModal(
|
|
104
|
+
title,
|
|
105
|
+
message,
|
|
106
|
+
onConfirm = null,
|
|
107
|
+
onCancel = null
|
|
108
|
+
) {
|
|
109
|
+
const modal = document.getElementById("alertModal");
|
|
110
|
+
if (!modal) return;
|
|
111
|
+
|
|
112
|
+
const titleEl = document.getElementById("alertModalTitle");
|
|
113
|
+
const bodyEl = document.getElementById("alertModalBody");
|
|
114
|
+
const confirmBtn = document.getElementById("alertModalConfirm");
|
|
115
|
+
const cancelBtn = document.getElementById("alertModalCancel");
|
|
116
|
+
|
|
117
|
+
if (titleEl) titleEl.textContent = title;
|
|
118
|
+
if (bodyEl) bodyEl.textContent = message;
|
|
119
|
+
|
|
120
|
+
// 設置確認按鈕
|
|
121
|
+
if (confirmBtn) {
|
|
122
|
+
confirmBtn.onclick = () => {
|
|
123
|
+
hideAlertModal();
|
|
124
|
+
if (onConfirm) onConfirm();
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 設置取消按鈕
|
|
129
|
+
if (cancelBtn) {
|
|
130
|
+
if (onCancel) {
|
|
131
|
+
cancelBtn.style.display = "block";
|
|
132
|
+
cancelBtn.onclick = () => {
|
|
133
|
+
hideAlertModal();
|
|
134
|
+
onCancel();
|
|
135
|
+
};
|
|
136
|
+
} else {
|
|
137
|
+
cancelBtn.style.display = "none";
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
modal.classList.add("show");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 隱藏提醒彈窗
|
|
146
|
+
*/
|
|
147
|
+
export function hideAlertModal() {
|
|
148
|
+
const modal = document.getElementById("alertModal");
|
|
149
|
+
if (modal) {
|
|
150
|
+
modal.classList.remove("show");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 顯示載入遮罩
|
|
156
|
+
* @param {string} text - 載入文字
|
|
157
|
+
*/
|
|
158
|
+
export function showLoadingOverlay(text = "處理中...") {
|
|
159
|
+
const overlay = document.getElementById("loadingOverlay");
|
|
160
|
+
const loadingText = document.getElementById("loadingText");
|
|
161
|
+
|
|
162
|
+
if (loadingText) loadingText.textContent = text;
|
|
163
|
+
if (overlay) overlay.style.display = "flex";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 隱藏載入遮罩
|
|
168
|
+
*/
|
|
169
|
+
export function hideLoadingOverlay() {
|
|
170
|
+
const overlay = document.getElementById("loadingOverlay");
|
|
171
|
+
if (overlay) overlay.style.display = "none";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 顯示專案資訊
|
|
176
|
+
* @param {string} projectName - 專案名稱
|
|
177
|
+
* @param {string} projectPath - 專案路徑
|
|
178
|
+
*/
|
|
179
|
+
export function displayProjectInfo(projectName, projectPath) {
|
|
180
|
+
const projectInfoEl = document.getElementById("projectInfo");
|
|
181
|
+
if (!projectInfoEl) return;
|
|
182
|
+
|
|
183
|
+
if (projectName || projectPath) {
|
|
184
|
+
const name = projectName || "未命名專案";
|
|
185
|
+
const path = projectPath ? ` (${projectPath})` : "";
|
|
186
|
+
projectInfoEl.innerHTML = `<span class="icon">📁</span> ${name}${path}`;
|
|
187
|
+
projectInfoEl.title = projectPath || projectName || "";
|
|
188
|
+
} else {
|
|
189
|
+
projectInfoEl.innerHTML = "";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 顯示 AI 訊息
|
|
195
|
+
* @param {string} message - 訊息內容 (Markdown)
|
|
196
|
+
*/
|
|
197
|
+
export function displayAIMessage(message) {
|
|
198
|
+
const displayEl = document.getElementById("aiMessageDisplay");
|
|
199
|
+
if (!displayEl) return;
|
|
200
|
+
|
|
201
|
+
const htmlContent = marked.parse(message);
|
|
202
|
+
displayEl.innerHTML = `<div class="ai-message-content">${htmlContent}</div>`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* 更新字元計數
|
|
207
|
+
*/
|
|
208
|
+
export function updateCharCount() {
|
|
209
|
+
const textEl = document.getElementById("feedbackText");
|
|
210
|
+
const countEl = document.getElementById("charCount");
|
|
211
|
+
|
|
212
|
+
if (textEl && countEl) {
|
|
213
|
+
countEl.textContent = `${textEl.value.length} 字元`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 截斷過長的文字
|
|
219
|
+
* @param {string|Object} text - 要截斷的文字
|
|
220
|
+
* @param {number} maxLength - 最大長度
|
|
221
|
+
* @returns {string} - 截斷後的文字
|
|
222
|
+
*/
|
|
223
|
+
export function truncateResult(text, maxLength = 500) {
|
|
224
|
+
if (typeof text !== "string") {
|
|
225
|
+
text = JSON.stringify(text, null, 2);
|
|
226
|
+
}
|
|
227
|
+
if (text.length > maxLength) {
|
|
228
|
+
return text.substring(0, maxLength) + "\n... (已截斷)";
|
|
229
|
+
}
|
|
230
|
+
return text;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export default {
|
|
234
|
+
escapeHtml,
|
|
235
|
+
escapeRegex,
|
|
236
|
+
showToast,
|
|
237
|
+
formatApiError,
|
|
238
|
+
showAlertModal,
|
|
239
|
+
hideAlertModal,
|
|
240
|
+
showLoadingOverlay,
|
|
241
|
+
hideLoadingOverlay,
|
|
242
|
+
displayProjectInfo,
|
|
243
|
+
displayAIMessage,
|
|
244
|
+
updateCharCount,
|
|
245
|
+
truncateResult,
|
|
246
|
+
};
|