funda-ui 4.6.222 → 4.6.333
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/Chatbox/index.css +18 -1
- package/Chatbox/index.d.ts +4 -0
- package/Chatbox/index.js +250 -157
- package/Utils/format-string.d.ts +64 -0
- package/Utils/format-string.js +157 -0
- package/lib/cjs/Chatbox/index.d.ts +4 -0
- package/lib/cjs/Chatbox/index.js +250 -157
- package/lib/cjs/Utils/format-string.d.ts +64 -0
- package/lib/cjs/Utils/format-string.js +157 -0
- package/lib/css/Chatbox/index.css +18 -1
- package/lib/esm/Chatbox/index.scss +27 -6
- package/lib/esm/Chatbox/index.tsx +83 -8
- package/lib/esm/Utils/libs/format-string.ts +106 -0
- package/package.json +1 -1
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String formatting utility functions
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Remove all special characters except space from a string
|
|
6
|
+
* @param {string} input - The input string to process
|
|
7
|
+
* @returns {string} The processed string
|
|
8
|
+
*/
|
|
9
|
+
declare function rmSpec(input: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Allow only numbers and letters in a string
|
|
12
|
+
* @param {string} input - The input string to process
|
|
13
|
+
* @returns {string} The processed string
|
|
14
|
+
*/
|
|
15
|
+
declare function onlyNumAndLetter(input: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Remove all spaces including those in the middle
|
|
18
|
+
* @param {string} input - The input string to process
|
|
19
|
+
* @returns {string} The processed string
|
|
20
|
+
*/
|
|
21
|
+
declare function rmAllSpace(input: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Remove whitespace from both sides of a string
|
|
24
|
+
* @param {string} input - The input string to process
|
|
25
|
+
* @returns {string} The processed string
|
|
26
|
+
*/
|
|
27
|
+
declare function trimAll(input: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Replace multiple spaces with a single space
|
|
30
|
+
* @param {string} input - The input string to process
|
|
31
|
+
* @returns {string} The processed string
|
|
32
|
+
*/
|
|
33
|
+
declare function multiSpacesToSingle(input: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Convert HTML text to plain text
|
|
36
|
+
* @param {string} input - The input string to process
|
|
37
|
+
* @returns {string} The processed string
|
|
38
|
+
*/
|
|
39
|
+
declare function htmlToPlain(input: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Strip HTML tags and their content
|
|
42
|
+
* @param {string} input - The input string to process
|
|
43
|
+
* @returns {string} The processed string
|
|
44
|
+
*/
|
|
45
|
+
declare function stripTagsAndContent(input: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Remove first and last slash from a URL
|
|
48
|
+
* @param {string} input - The input URL to process
|
|
49
|
+
* @returns {string} The processed URL
|
|
50
|
+
*/
|
|
51
|
+
declare function removeFirstLastSlash(input: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Remove trailing slash from a URL
|
|
54
|
+
* @param {string} input - The input URL to process
|
|
55
|
+
* @returns {string} The processed URL
|
|
56
|
+
*/
|
|
57
|
+
declare function removeTrailingSlash(input: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Remove first slash from a URL
|
|
60
|
+
* @param {string} input - The input URL to process
|
|
61
|
+
* @returns {string} The processed URL
|
|
62
|
+
*/
|
|
63
|
+
declare function removeFirstSlash(input: string): string;
|
|
64
|
+
export { rmSpec, onlyNumAndLetter, rmAllSpace, trimAll, multiSpacesToSingle, htmlToPlain, stripTagsAndContent, removeFirstLastSlash, removeTrailingSlash, removeFirstSlash };
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
(function webpackUniversalModuleDefinition(root, factory) {
|
|
2
|
+
if(typeof exports === 'object' && typeof module === 'object')
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
else if(typeof define === 'function' && define.amd)
|
|
5
|
+
define([], factory);
|
|
6
|
+
else if(typeof exports === 'object')
|
|
7
|
+
exports["RPB"] = factory();
|
|
8
|
+
else
|
|
9
|
+
root["RPB"] = factory();
|
|
10
|
+
})(this, () => {
|
|
11
|
+
return /******/ (() => { // webpackBootstrap
|
|
12
|
+
/******/ "use strict";
|
|
13
|
+
/******/ // The require scope
|
|
14
|
+
/******/ var __webpack_require__ = {};
|
|
15
|
+
/******/
|
|
16
|
+
/************************************************************************/
|
|
17
|
+
/******/ /* webpack/runtime/define property getters */
|
|
18
|
+
/******/ (() => {
|
|
19
|
+
/******/ // define getter functions for harmony exports
|
|
20
|
+
/******/ __webpack_require__.d = (exports, definition) => {
|
|
21
|
+
/******/ for(var key in definition) {
|
|
22
|
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
23
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
24
|
+
/******/ }
|
|
25
|
+
/******/ }
|
|
26
|
+
/******/ };
|
|
27
|
+
/******/ })();
|
|
28
|
+
/******/
|
|
29
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
30
|
+
/******/ (() => {
|
|
31
|
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
32
|
+
/******/ })();
|
|
33
|
+
/******/
|
|
34
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
35
|
+
/******/ (() => {
|
|
36
|
+
/******/ // define __esModule on exports
|
|
37
|
+
/******/ __webpack_require__.r = (exports) => {
|
|
38
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
39
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
40
|
+
/******/ }
|
|
41
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
42
|
+
/******/ };
|
|
43
|
+
/******/ })();
|
|
44
|
+
/******/
|
|
45
|
+
/************************************************************************/
|
|
46
|
+
var __webpack_exports__ = {};
|
|
47
|
+
__webpack_require__.r(__webpack_exports__);
|
|
48
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
49
|
+
/* harmony export */ "htmlToPlain": () => (/* binding */ htmlToPlain),
|
|
50
|
+
/* harmony export */ "multiSpacesToSingle": () => (/* binding */ multiSpacesToSingle),
|
|
51
|
+
/* harmony export */ "onlyNumAndLetter": () => (/* binding */ onlyNumAndLetter),
|
|
52
|
+
/* harmony export */ "removeFirstLastSlash": () => (/* binding */ removeFirstLastSlash),
|
|
53
|
+
/* harmony export */ "removeFirstSlash": () => (/* binding */ removeFirstSlash),
|
|
54
|
+
/* harmony export */ "removeTrailingSlash": () => (/* binding */ removeTrailingSlash),
|
|
55
|
+
/* harmony export */ "rmAllSpace": () => (/* binding */ rmAllSpace),
|
|
56
|
+
/* harmony export */ "rmSpec": () => (/* binding */ rmSpec),
|
|
57
|
+
/* harmony export */ "stripTagsAndContent": () => (/* binding */ stripTagsAndContent),
|
|
58
|
+
/* harmony export */ "trimAll": () => (/* binding */ trimAll)
|
|
59
|
+
/* harmony export */ });
|
|
60
|
+
/**
|
|
61
|
+
* String formatting utility functions
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Remove all special characters except space from a string
|
|
66
|
+
* @param {string} input - The input string to process
|
|
67
|
+
* @returns {string} The processed string
|
|
68
|
+
*/
|
|
69
|
+
function rmSpec(input) {
|
|
70
|
+
return input.replace(/[^a-zA-Z0-9 \u4E00-\u9FFF]/g, "");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Allow only numbers and letters in a string
|
|
75
|
+
* @param {string} input - The input string to process
|
|
76
|
+
* @returns {string} The processed string
|
|
77
|
+
*/
|
|
78
|
+
function onlyNumAndLetter(input) {
|
|
79
|
+
return input.replace(/[^a-zA-Z0-9 ]/g, "");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Remove all spaces including those in the middle
|
|
84
|
+
* @param {string} input - The input string to process
|
|
85
|
+
* @returns {string} The processed string
|
|
86
|
+
*/
|
|
87
|
+
function rmAllSpace(input) {
|
|
88
|
+
return input.replace(/\s/g, "");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Remove whitespace from both sides of a string
|
|
93
|
+
* @param {string} input - The input string to process
|
|
94
|
+
* @returns {string} The processed string
|
|
95
|
+
*/
|
|
96
|
+
function trimAll(input) {
|
|
97
|
+
return input.replace(/(^\s+)|(\s+$)/g, "");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Replace multiple spaces with a single space
|
|
102
|
+
* @param {string} input - The input string to process
|
|
103
|
+
* @returns {string} The processed string
|
|
104
|
+
*/
|
|
105
|
+
function multiSpacesToSingle(input) {
|
|
106
|
+
return input.replace(/\s+(\W)/g, ' ');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Convert HTML text to plain text
|
|
111
|
+
* @param {string} input - The input string to process
|
|
112
|
+
* @returns {string} The processed string
|
|
113
|
+
*/
|
|
114
|
+
function htmlToPlain(input) {
|
|
115
|
+
return input.replace(/(<([^>]+)>)/ig, '');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Strip HTML tags and their content
|
|
120
|
+
* @param {string} input - The input string to process
|
|
121
|
+
* @returns {string} The processed string
|
|
122
|
+
*/
|
|
123
|
+
function stripTagsAndContent(input) {
|
|
124
|
+
return input.replace(/<\/?[^>]+(>|$)(.*?)<\/?[^>]+(>|$)/ig, '');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Remove first and last slash from a URL
|
|
129
|
+
* @param {string} input - The input URL to process
|
|
130
|
+
* @returns {string} The processed URL
|
|
131
|
+
*/
|
|
132
|
+
function removeFirstLastSlash(input) {
|
|
133
|
+
return input.replace(/^\/|\/$/g, '');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Remove trailing slash from a URL
|
|
138
|
+
* @param {string} input - The input URL to process
|
|
139
|
+
* @returns {string} The processed URL
|
|
140
|
+
*/
|
|
141
|
+
function removeTrailingSlash(input) {
|
|
142
|
+
return input.replace(/\/+$/, '');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Remove first slash from a URL
|
|
147
|
+
* @param {string} input - The input URL to process
|
|
148
|
+
* @returns {string} The processed URL
|
|
149
|
+
*/
|
|
150
|
+
function removeFirstSlash(input) {
|
|
151
|
+
return input.replace(/\//, '');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/******/ return __webpack_exports__;
|
|
155
|
+
/******/ })()
|
|
156
|
+
;
|
|
157
|
+
});
|
|
@@ -198,6 +198,7 @@
|
|
|
198
198
|
margin-bottom: 10px;
|
|
199
199
|
font-size: 13px;
|
|
200
200
|
margin-right: 0;
|
|
201
|
+
/* copy button */
|
|
201
202
|
}
|
|
202
203
|
.custom-chatbox-container .messages img, .custom-chatbox-container .messages svg, .custom-chatbox-container .messages video, .custom-chatbox-container .messages canvas, .custom-chatbox-container .messages audio, .custom-chatbox-container .messages iframe, .custom-chatbox-container .messages embed, .custom-chatbox-container .messages object {
|
|
203
204
|
display: inline;
|
|
@@ -208,10 +209,11 @@
|
|
|
208
209
|
.custom-chatbox-container .messages::-webkit-scrollbar-thumb {
|
|
209
210
|
background: rgba(0, 0, 0, 0.2);
|
|
210
211
|
}
|
|
211
|
-
.custom-chatbox-container .messages > div {
|
|
212
|
+
.custom-chatbox-container .messages > div:not(.newchat-btn) {
|
|
212
213
|
margin: 5px 0;
|
|
213
214
|
padding: 3px 5px;
|
|
214
215
|
border-radius: 0.35rem;
|
|
216
|
+
position: relative;
|
|
215
217
|
}
|
|
216
218
|
.custom-chatbox-container .messages p {
|
|
217
219
|
margin: 3px 0;
|
|
@@ -284,6 +286,21 @@
|
|
|
284
286
|
background: transparent;
|
|
285
287
|
padding-top: 0;
|
|
286
288
|
}
|
|
289
|
+
.custom-chatbox-container .messages .copy-btn {
|
|
290
|
+
position: absolute;
|
|
291
|
+
left: calc(var(--custom-chatbox-content-w) - 0.7rem);
|
|
292
|
+
bottom: 0.5rem;
|
|
293
|
+
z-index: 1;
|
|
294
|
+
background: transparent;
|
|
295
|
+
border: none;
|
|
296
|
+
padding: 4px;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
opacity: 0.6;
|
|
299
|
+
transition: opacity 0.2s;
|
|
300
|
+
}
|
|
301
|
+
.custom-chatbox-container .messages .copy-btn:hover {
|
|
302
|
+
opacity: 1;
|
|
303
|
+
}
|
|
287
304
|
.custom-chatbox-container .msg-dotted-loader-container {
|
|
288
305
|
font-weight: normal;
|
|
289
306
|
transform: translateY(-5px);
|
|
@@ -167,6 +167,8 @@
|
|
|
167
167
|
--custom-chatbox-content-html-elem-border-color: #ddd;
|
|
168
168
|
--custom-chatbox-content-html-elem-bg: rgba(0,0,0,.05);
|
|
169
169
|
|
|
170
|
+
|
|
171
|
+
|
|
170
172
|
min-width: var(--custom-chatbox-w);
|
|
171
173
|
max-width: var(--custom-chatbox-w);
|
|
172
174
|
margin: auto;
|
|
@@ -243,10 +245,11 @@
|
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
|
|
246
|
-
> div {
|
|
248
|
+
> div:not(.newchat-btn) {
|
|
247
249
|
margin: 5px 0;
|
|
248
250
|
padding: 3px 5px;
|
|
249
251
|
border-radius: 0.35rem;
|
|
252
|
+
position: relative;
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
p {
|
|
@@ -298,6 +301,7 @@
|
|
|
298
301
|
border-radius: 0.35rem;
|
|
299
302
|
|
|
300
303
|
|
|
304
|
+
|
|
301
305
|
thead {
|
|
302
306
|
background: var(--custom-chatbox-content-html-elem-bg);
|
|
303
307
|
|
|
@@ -313,6 +317,7 @@
|
|
|
313
317
|
}
|
|
314
318
|
}
|
|
315
319
|
}
|
|
320
|
+
|
|
316
321
|
|
|
317
322
|
}
|
|
318
323
|
|
|
@@ -345,6 +350,26 @@
|
|
|
345
350
|
|
|
346
351
|
}
|
|
347
352
|
}
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
/* copy button */
|
|
356
|
+
.copy-btn {
|
|
357
|
+
position: absolute;
|
|
358
|
+
left: calc(var(--custom-chatbox-content-w) - .7rem);
|
|
359
|
+
bottom: 0.5rem;
|
|
360
|
+
z-index: 1;
|
|
361
|
+
background: transparent;
|
|
362
|
+
border: none;
|
|
363
|
+
padding: 4px;
|
|
364
|
+
cursor: pointer;
|
|
365
|
+
opacity: 0.6;
|
|
366
|
+
transition: opacity 0.2s;
|
|
367
|
+
|
|
368
|
+
&:hover {
|
|
369
|
+
opacity: 1;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
348
373
|
}
|
|
349
374
|
|
|
350
375
|
/* dot loading */
|
|
@@ -523,7 +548,7 @@
|
|
|
523
548
|
left: 50%;
|
|
524
549
|
transform: translateX(-50%);
|
|
525
550
|
z-index: 1;
|
|
526
|
-
|
|
551
|
+
|
|
527
552
|
> button {
|
|
528
553
|
padding: 3px 6px;
|
|
529
554
|
background-color: var(--custom-chatbox-newchat-btn-color);
|
|
@@ -683,8 +708,6 @@
|
|
|
683
708
|
}
|
|
684
709
|
|
|
685
710
|
|
|
686
|
-
|
|
687
|
-
|
|
688
711
|
/* default questions */
|
|
689
712
|
.default-questions-title {
|
|
690
713
|
margin-bottom: .5rem;
|
|
@@ -712,8 +735,6 @@
|
|
|
712
735
|
}
|
|
713
736
|
|
|
714
737
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
738
|
|
|
718
739
|
|
|
719
740
|
}
|
|
@@ -12,6 +12,7 @@ import useClickOutside from 'funda-utils/dist/cjs/useClickOutside';
|
|
|
12
12
|
import { htmlEncode } from 'funda-utils/dist/cjs/sanitize';
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
|
|
15
16
|
// loader
|
|
16
17
|
import PureLoader from './PureLoader';
|
|
17
18
|
import TypingEffect from "./TypingEffect";
|
|
@@ -114,10 +115,14 @@ export type ChatboxProps = {
|
|
|
114
115
|
newChatButton?: FloatingButton;
|
|
115
116
|
customMethods?: CustomMethod[]; // [{"name": "method1", "func": "() => { console.log('test'); }"}, ...]
|
|
116
117
|
defaultQuestions?: QuestionData;
|
|
118
|
+
showCopyBtn?: boolean; // Whether to show copy button for each reply
|
|
119
|
+
autoCopyReply?: boolean; // Whether to automatically copy reply to clipboard
|
|
117
120
|
customRequest?: CustomRequestFunction;
|
|
118
121
|
renderParser?: (input: string) => Promise<string>;
|
|
119
122
|
requestBodyFormatter?: (body: any, contextData: Record<string, any>, conversationHistory: MessageDetail[]) => Promise<Record<string, any>>;
|
|
123
|
+
copiedContentFormatter?: (string: string) => string;
|
|
120
124
|
nameFormatter?: (input: string) => string;
|
|
125
|
+
onCopyCallback?: (res: Record<string, any>) => void;
|
|
121
126
|
onQuestionClick?: (text: string, methods: Record<string, Function>) => void;
|
|
122
127
|
onInputChange?: (controlRef: React.RefObject<any>, val: string) => any;
|
|
123
128
|
onInputCallback?: (input: string) => Promise<string>;
|
|
@@ -296,9 +301,13 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
296
301
|
maxHistoryLength,
|
|
297
302
|
customRequest,
|
|
298
303
|
onQuestionClick,
|
|
304
|
+
onCopyCallback,
|
|
299
305
|
renderParser,
|
|
300
306
|
requestBodyFormatter,
|
|
307
|
+
copiedContentFormatter,
|
|
301
308
|
nameFormatter,
|
|
309
|
+
showCopyBtn,
|
|
310
|
+
autoCopyReply,
|
|
302
311
|
onInputChange,
|
|
303
312
|
onInputCallback,
|
|
304
313
|
onChunk,
|
|
@@ -370,9 +379,13 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
370
379
|
newChatButton,
|
|
371
380
|
customRequest,
|
|
372
381
|
onQuestionClick,
|
|
382
|
+
onCopyCallback,
|
|
373
383
|
renderParser,
|
|
374
384
|
requestBodyFormatter,
|
|
385
|
+
copiedContentFormatter,
|
|
375
386
|
nameFormatter,
|
|
387
|
+
showCopyBtn,
|
|
388
|
+
autoCopyReply,
|
|
376
389
|
onInputChange,
|
|
377
390
|
onInputCallback,
|
|
378
391
|
onChunk,
|
|
@@ -393,6 +406,57 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
393
406
|
|
|
394
407
|
}
|
|
395
408
|
|
|
409
|
+
//================================================================
|
|
410
|
+
// Clipboard
|
|
411
|
+
//================================================================
|
|
412
|
+
const chatboxCopyToClipboard = async (text: string) => {
|
|
413
|
+
|
|
414
|
+
let _content: string = text;
|
|
415
|
+
if (typeof args().copiedContentFormatter === 'function') {
|
|
416
|
+
_content = args().copiedContentFormatter(text);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
try {
|
|
420
|
+
// Try using the modern Clipboard API first
|
|
421
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
422
|
+
await navigator.clipboard.writeText(_content);
|
|
423
|
+
args().onCopyCallback?.({
|
|
424
|
+
success: true,
|
|
425
|
+
message: 'Text copied to clipboard',
|
|
426
|
+
});
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Fallback for older browsers
|
|
431
|
+
const textArea = document.createElement('textarea');
|
|
432
|
+
textArea.value = _content;
|
|
433
|
+
textArea.style.position = 'fixed';
|
|
434
|
+
textArea.style.left = '-999999px';
|
|
435
|
+
textArea.style.top = '-999999px';
|
|
436
|
+
document.body.appendChild(textArea);
|
|
437
|
+
textArea.focus();
|
|
438
|
+
textArea.select();
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
document.execCommand('copy');
|
|
442
|
+
textArea.remove();
|
|
443
|
+
args().onCopyCallback?.({
|
|
444
|
+
success: true,
|
|
445
|
+
message: 'Text copied to clipboard',
|
|
446
|
+
});
|
|
447
|
+
return true;
|
|
448
|
+
} catch (err) {
|
|
449
|
+
textArea.remove();
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
} catch (err) {
|
|
453
|
+
args().onCopyCallback?.({
|
|
454
|
+
success: false,
|
|
455
|
+
message: `Failed to copy text: ${err}`,
|
|
456
|
+
});
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
396
460
|
|
|
397
461
|
|
|
398
462
|
//================================================================
|
|
@@ -820,6 +884,11 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
820
884
|
// Update the message list state
|
|
821
885
|
setMsgList((prevMessages) => [...prevMessages, newMessage]);
|
|
822
886
|
|
|
887
|
+
// Auto copy reply if enabled
|
|
888
|
+
if (args().autoCopyReply && sender === args().answerNameRes) {
|
|
889
|
+
chatboxCopyToClipboard(content);
|
|
890
|
+
}
|
|
891
|
+
|
|
823
892
|
};
|
|
824
893
|
|
|
825
894
|
const sendMessage = async () => {
|
|
@@ -1155,7 +1224,10 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1155
1224
|
}
|
|
1156
1225
|
}, [props.defaultMessages]);
|
|
1157
1226
|
|
|
1158
|
-
|
|
1227
|
+
useEffect(() => {
|
|
1228
|
+
// Bind chatboxCopyToClipboard to window so it can be called in HTML code
|
|
1229
|
+
(window as any).chatboxCopyToClipboard = chatboxCopyToClipboard;
|
|
1230
|
+
}, []);
|
|
1159
1231
|
|
|
1160
1232
|
return (
|
|
1161
1233
|
<>
|
|
@@ -1220,39 +1292,42 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1220
1292
|
|
|
1221
1293
|
</> : null}
|
|
1222
1294
|
{/**------------- /NO DATA -------------*/}
|
|
1223
|
-
|
|
1224
|
-
|
|
1295
|
+
|
|
1225
1296
|
|
|
1226
1297
|
{/**------------- MESSAGES LIST -------------*/}
|
|
1227
1298
|
<div className="messages" ref={msgContainerRef}>
|
|
1228
1299
|
|
|
1229
1300
|
{msgList.map((msg, index) => {
|
|
1230
1301
|
|
|
1302
|
+
const copyTargetId = `${args().prefix || 'custom-'}chatbox-content--${chatId}${index}`;
|
|
1231
1303
|
const isAnimProgress = tempAnimText !== '' && msg.sender !== args().questionNameRes && index === msgList.length - 1 && loading;
|
|
1232
1304
|
const hasAnimated = animatedMessagesRef.current.has(index);
|
|
1233
1305
|
|
|
1234
1306
|
// Mark the message as animated;
|
|
1235
1307
|
animatedMessagesRef.current.add(index);
|
|
1236
1308
|
|
|
1309
|
+
const timeShow = `<span class="qa-timestamp">${msg.timestamp}</span>${args().showCopyBtn && msg.tag?.indexOf('[reply]') >= 0 ?(`<button class="copy-btn" onclick="window.chatboxCopyToClipboard(document.querySelector('#${copyTargetId} .qa-content-inner').innerHTML)"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M8 4v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7.242a2 2 0 0 0-.602-1.43L16.083 2.57A2 2 0 0 0 14.685 2H10a2 2 0 0 0-2 2z"/><path d="M16 18v2a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h2"/></svg></button>`) : ''}`;
|
|
1310
|
+
|
|
1237
1311
|
return <div key={index} className={msg.tag?.indexOf('[reply]') < 0 ? 'request' : 'reply'} style={{ display: isAnimProgress ? 'none' : '' }}>
|
|
1238
1312
|
<div className="qa-name" dangerouslySetInnerHTML={{ __html: `${msg.sender}` }}></div>
|
|
1239
1313
|
|
|
1314
|
+
|
|
1240
1315
|
{msg.sender === args().questionNameRes ? <>
|
|
1241
|
-
<div className="qa-content" dangerouslySetInnerHTML={{ __html:
|
|
1316
|
+
<div className="qa-content" id={copyTargetId} dangerouslySetInnerHTML={{ __html: `<div class="qa-content-inner">${msg.content}</div> ${timeShow}` }}></div>
|
|
1242
1317
|
</> : <>
|
|
1243
1318
|
|
|
1244
1319
|
{enableStreamMode ? <>
|
|
1245
|
-
<div className="qa-content" dangerouslySetInnerHTML={{ __html:
|
|
1320
|
+
<div className="qa-content" id={copyTargetId} dangerouslySetInnerHTML={{ __html: `<div class="qa-content-inner">${msg.content}</div> ${timeShow}` }}></div>
|
|
1246
1321
|
</> : <>
|
|
1247
|
-
<div className="qa-content">
|
|
1322
|
+
<div className="qa-content" id={copyTargetId}>
|
|
1248
1323
|
{hasAnimated ? (
|
|
1249
|
-
<div dangerouslySetInnerHTML={{ __html:
|
|
1324
|
+
<div dangerouslySetInnerHTML={{ __html: `<div class="qa-content-inner">${msg.content}</div> ${timeShow}` }}></div>
|
|
1250
1325
|
) : (
|
|
1251
1326
|
<TypingEffect
|
|
1252
1327
|
onUpdate={() => {
|
|
1253
1328
|
scrollToBottom();
|
|
1254
1329
|
}}
|
|
1255
|
-
content={
|
|
1330
|
+
content={`<div class="qa-content-inner">${msg.content}</div> ${timeShow}`}
|
|
1256
1331
|
speed={10}
|
|
1257
1332
|
/>
|
|
1258
1333
|
)}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String formatting utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Remove all special characters except space from a string
|
|
7
|
+
* @param {string} input - The input string to process
|
|
8
|
+
* @returns {string} The processed string
|
|
9
|
+
*/
|
|
10
|
+
function rmSpec(input: string): string {
|
|
11
|
+
return input.replace(/[^a-zA-Z0-9 \u4E00-\u9FFF]/g, "");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Allow only numbers and letters in a string
|
|
16
|
+
* @param {string} input - The input string to process
|
|
17
|
+
* @returns {string} The processed string
|
|
18
|
+
*/
|
|
19
|
+
function onlyNumAndLetter(input: string): string {
|
|
20
|
+
return input.replace(/[^a-zA-Z0-9 ]/g, "");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Remove all spaces including those in the middle
|
|
25
|
+
* @param {string} input - The input string to process
|
|
26
|
+
* @returns {string} The processed string
|
|
27
|
+
*/
|
|
28
|
+
function rmAllSpace(input: string): string {
|
|
29
|
+
return input.replace(/\s/g, "");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Remove whitespace from both sides of a string
|
|
34
|
+
* @param {string} input - The input string to process
|
|
35
|
+
* @returns {string} The processed string
|
|
36
|
+
*/
|
|
37
|
+
function trimAll(input: string): string {
|
|
38
|
+
return input.replace(/(^\s+)|(\s+$)/g, "");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Replace multiple spaces with a single space
|
|
43
|
+
* @param {string} input - The input string to process
|
|
44
|
+
* @returns {string} The processed string
|
|
45
|
+
*/
|
|
46
|
+
function multiSpacesToSingle(input: string): string {
|
|
47
|
+
return input.replace(/\s+(\W)/g, ' ');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Convert HTML text to plain text
|
|
52
|
+
* @param {string} input - The input string to process
|
|
53
|
+
* @returns {string} The processed string
|
|
54
|
+
*/
|
|
55
|
+
function htmlToPlain(input: string): string {
|
|
56
|
+
return input.replace(/(<([^>]+)>)/ig, '');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Strip HTML tags and their content
|
|
61
|
+
* @param {string} input - The input string to process
|
|
62
|
+
* @returns {string} The processed string
|
|
63
|
+
*/
|
|
64
|
+
function stripTagsAndContent(input: string): string {
|
|
65
|
+
return input.replace(/<\/?[^>]+(>|$)(.*?)<\/?[^>]+(>|$)/ig, '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Remove first and last slash from a URL
|
|
70
|
+
* @param {string} input - The input URL to process
|
|
71
|
+
* @returns {string} The processed URL
|
|
72
|
+
*/
|
|
73
|
+
function removeFirstLastSlash(input: string): string {
|
|
74
|
+
return input.replace(/^\/|\/$/g, '');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Remove trailing slash from a URL
|
|
79
|
+
* @param {string} input - The input URL to process
|
|
80
|
+
* @returns {string} The processed URL
|
|
81
|
+
*/
|
|
82
|
+
function removeTrailingSlash(input: string): string {
|
|
83
|
+
return input.replace(/\/+$/, '');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Remove first slash from a URL
|
|
88
|
+
* @param {string} input - The input URL to process
|
|
89
|
+
* @returns {string} The processed URL
|
|
90
|
+
*/
|
|
91
|
+
function removeFirstSlash(input: string): string {
|
|
92
|
+
return input.replace(/\//, '');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export {
|
|
96
|
+
rmSpec,
|
|
97
|
+
onlyNumAndLetter,
|
|
98
|
+
rmAllSpace,
|
|
99
|
+
trimAll,
|
|
100
|
+
multiSpacesToSingle,
|
|
101
|
+
htmlToPlain,
|
|
102
|
+
stripTagsAndContent,
|
|
103
|
+
removeFirstLastSlash,
|
|
104
|
+
removeTrailingSlash,
|
|
105
|
+
removeFirstSlash
|
|
106
|
+
};
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "UIUX Lab",
|
|
3
3
|
"email": "uiuxlab@gmail.com",
|
|
4
4
|
"name": "funda-ui",
|
|
5
|
-
"version": "4.6.
|
|
5
|
+
"version": "4.6.333",
|
|
6
6
|
"description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|