funda-ui 4.7.815 → 4.7.850
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.js +58 -28
- package/lib/cjs/Chatbox/index.js +58 -28
- package/lib/esm/Chatbox/index.tsx +77 -29
- package/lib/esm/Chatbox/utils/func.ts +1 -1
- package/package.json +1 -1
package/Chatbox/index.js
CHANGED
|
@@ -7525,6 +7525,7 @@ var Chatbox = function Chatbox(props) {
|
|
|
7525
7525
|
//================================================================
|
|
7526
7526
|
// Voice Input
|
|
7527
7527
|
//================================================================
|
|
7528
|
+
var pressStartTimeRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(0); // Record the time when the button was pressed.
|
|
7528
7529
|
var DEFAULT_VOICE_REQUEST_BODY_TEMPLATE = "{\n \"common\": {\n \"app_id\": \"{appId}\"\n },\n \"business\": {\n \"language\": \"{lang}\",\n \"domain\": \"iat\",\n \"accent\": \"mandarin\",\n \"dwa\": \"wpgs\"\n },\n \"data\": {\n \"status\": 0,\n \"format\": \"{format}\",\n \"encoding\": \"{encoding}\"\n }\n }";
|
|
7529
7530
|
var DEFAULT_AUDIO_BODY_TEMPLATE = "{\n \"data\": {\n \"status\": 1,\n \"format\": \"{format}\",\n \"encoding\": \"{encoding}\",\n \"audio\": \"{audioData}\"\n }\n }";
|
|
7530
7531
|
var _useState29 = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(false),
|
|
@@ -7746,18 +7747,39 @@ var Chatbox = function Chatbox(props) {
|
|
|
7746
7747
|
}));
|
|
7747
7748
|
ws.onmessage = function (e) {
|
|
7748
7749
|
var jsonData = JSON.parse(e.data);
|
|
7749
|
-
if (jsonData.data
|
|
7750
|
-
var data = jsonData.data
|
|
7751
|
-
var
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
if (
|
|
7756
|
-
|
|
7757
|
-
|
|
7750
|
+
if (jsonData.data) {
|
|
7751
|
+
var data = jsonData.data;
|
|
7752
|
+
var extractorTemplate = config.voiceResponseExtractor || "{{if data.result && data.result.pgs === \"apd\"}}resultText += resultTextTemp;{{/if}}{{resultTextTemp = getValueByPath(data, \"result.ws[].cw[0].w\");}}";
|
|
7753
|
+
|
|
7754
|
+
// you could use `if (data.result && data.result.pgs === "apd") { resultText += resultTextTemp;} resultTextTemp = data.result.ws.map(w => w.cw[0].w).join("");`
|
|
7755
|
+
|
|
7756
|
+
if (extractorTemplate) {
|
|
7757
|
+
try {
|
|
7758
|
+
// Construct the execution function, passing in the context variables
|
|
7759
|
+
// Syntax Translation: Convert {{if}} to standard JS
|
|
7760
|
+
// Support: {{if}}, {{else}}, {{/if}}, and direct expressions {{...}}
|
|
7761
|
+
var jsScript = extractorTemplate.replace(/{{if\s+(.+?)}}/g, 'if ($1) {').replace(/{{else}}/g, '} else {').replace(/{{\/if}}/g, '}').replace(/{{(.+?)}}/g, '$1');
|
|
7762
|
+
|
|
7763
|
+
/**
|
|
7764
|
+
* Sandbox Execution Environment
|
|
7765
|
+
* @param data - The source data (jsonData.data)
|
|
7766
|
+
* @param resultText - Confirmed text from previous turns
|
|
7767
|
+
* @param resultTextTemp - Temporary text from the current turn
|
|
7768
|
+
* @param getValueByPath - Helper for safe property access
|
|
7769
|
+
*/
|
|
7770
|
+
var resolver = new Function('data', 'resultText', 'resultTextTemp', 'getValueByPath', "\n try {\n ".concat(jsScript, "\n } catch (e) {\n console.error(\"Voice Template Runtime Error:\", e);\n }\n // Crucial: Return updated values to synchronize with component scope\n return { resultText, resultTextTemp };\n "));
|
|
7771
|
+
|
|
7772
|
+
// Execute and update local variables
|
|
7773
|
+
var updated = resolver(data, resultText, resultTextTemp, getValueByPath);
|
|
7774
|
+
resultText = updated.resultText;
|
|
7775
|
+
resultTextTemp = updated.resultTextTemp;
|
|
7776
|
+
} catch (err) {
|
|
7777
|
+
console.error("--> [ERROR] Failed to parse property: [voiceResponseExtractor]");
|
|
7778
|
+
interruptVoiceInput();
|
|
7758
7779
|
}
|
|
7759
|
-
resultTextTemp = extractedStr;
|
|
7760
7780
|
}
|
|
7781
|
+
|
|
7782
|
+
//
|
|
7761
7783
|
var currentFullText = resultText + resultTextTemp;
|
|
7762
7784
|
|
|
7763
7785
|
// Store to Ref for use by stopVoiceInput
|
|
@@ -8193,20 +8215,30 @@ var Chatbox = function Chatbox(props) {
|
|
|
8193
8215
|
var holdToTalk = typeof (voiceConfig === null || voiceConfig === void 0 ? void 0 : voiceConfig.holdToTalk) === 'undefined' ? true : voiceConfig.holdToTalk;
|
|
8194
8216
|
if (enableVoiceInput) {
|
|
8195
8217
|
// Helper to stop recording safely
|
|
8196
|
-
var handleActionStop = function handleActionStop(e) {
|
|
8197
|
-
// Prevent firing both Mouse and Touch events on some devices
|
|
8198
|
-
if (e.cancelable) e.preventDefault();
|
|
8199
|
-
if (isVoiceInputActive) {
|
|
8200
|
-
stopVoiceInput();
|
|
8201
|
-
}
|
|
8202
|
-
};
|
|
8203
8218
|
var handleActionStart = function handleActionStart(e) {
|
|
8204
8219
|
if (loading) return;
|
|
8205
8220
|
if (e.cancelable) e.preventDefault();
|
|
8221
|
+
pressStartTimeRef.current = Date.now();
|
|
8206
8222
|
if (!isVoiceInputActive) {
|
|
8207
8223
|
startVoiceInput();
|
|
8208
8224
|
}
|
|
8209
8225
|
};
|
|
8226
|
+
var handleActionStop = function handleActionStop(e) {
|
|
8227
|
+
if (e.cancelable) e.preventDefault();
|
|
8228
|
+
var duration = Date.now() - pressStartTimeRef.current;
|
|
8229
|
+
|
|
8230
|
+
// If the press time is extremely short (e.g. less than 100ms),
|
|
8231
|
+
// it means that it is a mistakeal touch or extremely fast
|
|
8232
|
+
// Add a small delay to execute to ensure that startVoiceInput has finished initializing
|
|
8233
|
+
if (duration < 150) {
|
|
8234
|
+
// > 50, < 160
|
|
8235
|
+
setTimeout(function () {
|
|
8236
|
+
stopVoiceInput();
|
|
8237
|
+
}, 100);
|
|
8238
|
+
return;
|
|
8239
|
+
}
|
|
8240
|
+
stopVoiceInput();
|
|
8241
|
+
};
|
|
8210
8242
|
return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("div", {
|
|
8211
8243
|
className: "".concat(args().prefix || 'custom-', "chatbox-voice-input-btn-wrapper ").concat(isVoiceInputActive ? 'active' : '')
|
|
8212
8244
|
}, isVoiceInputActive ? /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("span", null) : null, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("button", {
|
|
@@ -8216,18 +8248,16 @@ var Chatbox = function Chatbox(props) {
|
|
|
8216
8248
|
userSelect: 'none'
|
|
8217
8249
|
}
|
|
8218
8250
|
|
|
8219
|
-
// Mobile
|
|
8220
|
-
|
|
8221
|
-
onTouchStart: !holdToTalk ? undefined : handleActionStart,
|
|
8222
|
-
onTouchEnd: !holdToTalk ? undefined : handleActionStop,
|
|
8223
|
-
onTouchCancel: !holdToTalk ? undefined : handleActionStop // Handle system interruptions
|
|
8224
|
-
|
|
8225
|
-
// Desktop
|
|
8226
|
-
,
|
|
8227
|
-
onMouseDown: !holdToTalk ? undefined : handleActionStart,
|
|
8228
|
-
onMouseUp: !holdToTalk ? undefined : handleActionStop,
|
|
8229
|
-
onMouseLeave: !holdToTalk ? undefined : handleActionStop // Handle mouse dragging out of button
|
|
8251
|
+
// Mobile & Desktop
|
|
8252
|
+
// (using Pointer to avoid the stop problem caused by mixing Touch and Mouse)
|
|
8230
8253
|
,
|
|
8254
|
+
onPointerDown: holdToTalk ? handleActionStart : undefined,
|
|
8255
|
+
onPointerUp: holdToTalk ? handleActionStop : undefined,
|
|
8256
|
+
onPointerCancel: holdToTalk ? handleActionStop : undefined,
|
|
8257
|
+
onPointerLeave: holdToTalk ? handleActionStop : undefined,
|
|
8258
|
+
onContextMenu: function onContextMenu(e) {
|
|
8259
|
+
return e.preventDefault();
|
|
8260
|
+
},
|
|
8231
8261
|
onClick: function onClick(e) {
|
|
8232
8262
|
e.preventDefault();
|
|
8233
8263
|
e.stopPropagation();
|
package/lib/cjs/Chatbox/index.js
CHANGED
|
@@ -7525,6 +7525,7 @@ var Chatbox = function Chatbox(props) {
|
|
|
7525
7525
|
//================================================================
|
|
7526
7526
|
// Voice Input
|
|
7527
7527
|
//================================================================
|
|
7528
|
+
var pressStartTimeRef = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useRef)(0); // Record the time when the button was pressed.
|
|
7528
7529
|
var DEFAULT_VOICE_REQUEST_BODY_TEMPLATE = "{\n \"common\": {\n \"app_id\": \"{appId}\"\n },\n \"business\": {\n \"language\": \"{lang}\",\n \"domain\": \"iat\",\n \"accent\": \"mandarin\",\n \"dwa\": \"wpgs\"\n },\n \"data\": {\n \"status\": 0,\n \"format\": \"{format}\",\n \"encoding\": \"{encoding}\"\n }\n }";
|
|
7529
7530
|
var DEFAULT_AUDIO_BODY_TEMPLATE = "{\n \"data\": {\n \"status\": 1,\n \"format\": \"{format}\",\n \"encoding\": \"{encoding}\",\n \"audio\": \"{audioData}\"\n }\n }";
|
|
7530
7531
|
var _useState29 = (0,external_root_React_commonjs2_react_commonjs_react_amd_react_.useState)(false),
|
|
@@ -7746,18 +7747,39 @@ var Chatbox = function Chatbox(props) {
|
|
|
7746
7747
|
}));
|
|
7747
7748
|
ws.onmessage = function (e) {
|
|
7748
7749
|
var jsonData = JSON.parse(e.data);
|
|
7749
|
-
if (jsonData.data
|
|
7750
|
-
var data = jsonData.data
|
|
7751
|
-
var
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
if (
|
|
7756
|
-
|
|
7757
|
-
|
|
7750
|
+
if (jsonData.data) {
|
|
7751
|
+
var data = jsonData.data;
|
|
7752
|
+
var extractorTemplate = config.voiceResponseExtractor || "{{if data.result && data.result.pgs === \"apd\"}}resultText += resultTextTemp;{{/if}}{{resultTextTemp = getValueByPath(data, \"result.ws[].cw[0].w\");}}";
|
|
7753
|
+
|
|
7754
|
+
// you could use `if (data.result && data.result.pgs === "apd") { resultText += resultTextTemp;} resultTextTemp = data.result.ws.map(w => w.cw[0].w).join("");`
|
|
7755
|
+
|
|
7756
|
+
if (extractorTemplate) {
|
|
7757
|
+
try {
|
|
7758
|
+
// Construct the execution function, passing in the context variables
|
|
7759
|
+
// Syntax Translation: Convert {{if}} to standard JS
|
|
7760
|
+
// Support: {{if}}, {{else}}, {{/if}}, and direct expressions {{...}}
|
|
7761
|
+
var jsScript = extractorTemplate.replace(/{{if\s+(.+?)}}/g, 'if ($1) {').replace(/{{else}}/g, '} else {').replace(/{{\/if}}/g, '}').replace(/{{(.+?)}}/g, '$1');
|
|
7762
|
+
|
|
7763
|
+
/**
|
|
7764
|
+
* Sandbox Execution Environment
|
|
7765
|
+
* @param data - The source data (jsonData.data)
|
|
7766
|
+
* @param resultText - Confirmed text from previous turns
|
|
7767
|
+
* @param resultTextTemp - Temporary text from the current turn
|
|
7768
|
+
* @param getValueByPath - Helper for safe property access
|
|
7769
|
+
*/
|
|
7770
|
+
var resolver = new Function('data', 'resultText', 'resultTextTemp', 'getValueByPath', "\n try {\n ".concat(jsScript, "\n } catch (e) {\n console.error(\"Voice Template Runtime Error:\", e);\n }\n // Crucial: Return updated values to synchronize with component scope\n return { resultText, resultTextTemp };\n "));
|
|
7771
|
+
|
|
7772
|
+
// Execute and update local variables
|
|
7773
|
+
var updated = resolver(data, resultText, resultTextTemp, getValueByPath);
|
|
7774
|
+
resultText = updated.resultText;
|
|
7775
|
+
resultTextTemp = updated.resultTextTemp;
|
|
7776
|
+
} catch (err) {
|
|
7777
|
+
console.error("--> [ERROR] Failed to parse property: [voiceResponseExtractor]");
|
|
7778
|
+
interruptVoiceInput();
|
|
7758
7779
|
}
|
|
7759
|
-
resultTextTemp = extractedStr;
|
|
7760
7780
|
}
|
|
7781
|
+
|
|
7782
|
+
//
|
|
7761
7783
|
var currentFullText = resultText + resultTextTemp;
|
|
7762
7784
|
|
|
7763
7785
|
// Store to Ref for use by stopVoiceInput
|
|
@@ -8193,20 +8215,30 @@ var Chatbox = function Chatbox(props) {
|
|
|
8193
8215
|
var holdToTalk = typeof (voiceConfig === null || voiceConfig === void 0 ? void 0 : voiceConfig.holdToTalk) === 'undefined' ? true : voiceConfig.holdToTalk;
|
|
8194
8216
|
if (enableVoiceInput) {
|
|
8195
8217
|
// Helper to stop recording safely
|
|
8196
|
-
var handleActionStop = function handleActionStop(e) {
|
|
8197
|
-
// Prevent firing both Mouse and Touch events on some devices
|
|
8198
|
-
if (e.cancelable) e.preventDefault();
|
|
8199
|
-
if (isVoiceInputActive) {
|
|
8200
|
-
stopVoiceInput();
|
|
8201
|
-
}
|
|
8202
|
-
};
|
|
8203
8218
|
var handleActionStart = function handleActionStart(e) {
|
|
8204
8219
|
if (loading) return;
|
|
8205
8220
|
if (e.cancelable) e.preventDefault();
|
|
8221
|
+
pressStartTimeRef.current = Date.now();
|
|
8206
8222
|
if (!isVoiceInputActive) {
|
|
8207
8223
|
startVoiceInput();
|
|
8208
8224
|
}
|
|
8209
8225
|
};
|
|
8226
|
+
var handleActionStop = function handleActionStop(e) {
|
|
8227
|
+
if (e.cancelable) e.preventDefault();
|
|
8228
|
+
var duration = Date.now() - pressStartTimeRef.current;
|
|
8229
|
+
|
|
8230
|
+
// If the press time is extremely short (e.g. less than 100ms),
|
|
8231
|
+
// it means that it is a mistakeal touch or extremely fast
|
|
8232
|
+
// Add a small delay to execute to ensure that startVoiceInput has finished initializing
|
|
8233
|
+
if (duration < 150) {
|
|
8234
|
+
// > 50, < 160
|
|
8235
|
+
setTimeout(function () {
|
|
8236
|
+
stopVoiceInput();
|
|
8237
|
+
}, 100);
|
|
8238
|
+
return;
|
|
8239
|
+
}
|
|
8240
|
+
stopVoiceInput();
|
|
8241
|
+
};
|
|
8210
8242
|
return /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("div", {
|
|
8211
8243
|
className: "".concat(args().prefix || 'custom-', "chatbox-voice-input-btn-wrapper ").concat(isVoiceInputActive ? 'active' : '')
|
|
8212
8244
|
}, isVoiceInputActive ? /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("span", null) : null, /*#__PURE__*/external_root_React_commonjs2_react_commonjs_react_amd_react_default().createElement("button", {
|
|
@@ -8216,18 +8248,16 @@ var Chatbox = function Chatbox(props) {
|
|
|
8216
8248
|
userSelect: 'none'
|
|
8217
8249
|
}
|
|
8218
8250
|
|
|
8219
|
-
// Mobile
|
|
8220
|
-
|
|
8221
|
-
onTouchStart: !holdToTalk ? undefined : handleActionStart,
|
|
8222
|
-
onTouchEnd: !holdToTalk ? undefined : handleActionStop,
|
|
8223
|
-
onTouchCancel: !holdToTalk ? undefined : handleActionStop // Handle system interruptions
|
|
8224
|
-
|
|
8225
|
-
// Desktop
|
|
8226
|
-
,
|
|
8227
|
-
onMouseDown: !holdToTalk ? undefined : handleActionStart,
|
|
8228
|
-
onMouseUp: !holdToTalk ? undefined : handleActionStop,
|
|
8229
|
-
onMouseLeave: !holdToTalk ? undefined : handleActionStop // Handle mouse dragging out of button
|
|
8251
|
+
// Mobile & Desktop
|
|
8252
|
+
// (using Pointer to avoid the stop problem caused by mixing Touch and Mouse)
|
|
8230
8253
|
,
|
|
8254
|
+
onPointerDown: holdToTalk ? handleActionStart : undefined,
|
|
8255
|
+
onPointerUp: holdToTalk ? handleActionStop : undefined,
|
|
8256
|
+
onPointerCancel: holdToTalk ? handleActionStop : undefined,
|
|
8257
|
+
onPointerLeave: holdToTalk ? handleActionStop : undefined,
|
|
8258
|
+
onContextMenu: function onContextMenu(e) {
|
|
8259
|
+
return e.preventDefault();
|
|
8260
|
+
},
|
|
8231
8261
|
onClick: function onClick(e) {
|
|
8232
8262
|
e.preventDefault();
|
|
8233
8263
|
e.stopPropagation();
|
|
@@ -16,6 +16,7 @@ import guid from 'funda-utils/dist/cjs/guid';
|
|
|
16
16
|
import { uint8arrayToBase64Str } from 'funda-utils/dist/cjs/buffer';
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
|
|
19
20
|
// loader
|
|
20
21
|
import PureLoader from './PureLoader';
|
|
21
22
|
import TypingEffect from './TypingEffect';
|
|
@@ -1379,6 +1380,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1379
1380
|
//================================================================
|
|
1380
1381
|
// Voice Input
|
|
1381
1382
|
//================================================================
|
|
1383
|
+
const pressStartTimeRef = useRef<number>(0); // Record the time when the button was pressed.
|
|
1382
1384
|
const DEFAULT_VOICE_REQUEST_BODY_TEMPLATE = `{
|
|
1383
1385
|
"common": {
|
|
1384
1386
|
"app_id": "{appId}"
|
|
@@ -1582,20 +1584,55 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1582
1584
|
|
|
1583
1585
|
ws.onmessage = (e) => {
|
|
1584
1586
|
const jsonData = JSON.parse(e.data);
|
|
1585
|
-
if (jsonData.data && jsonData.data.result) {
|
|
1586
|
-
const data = jsonData.data.result;
|
|
1587
1587
|
|
|
1588
|
-
|
|
1589
|
-
const
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
if (
|
|
1593
|
-
|
|
1594
|
-
|
|
1588
|
+
if (jsonData.data) {
|
|
1589
|
+
const data = jsonData.data;
|
|
1590
|
+
const extractorTemplate = config.voiceResponseExtractor || `{{if data.result && data.result.pgs === "apd"}}resultText += resultTextTemp;{{/if}}{{resultTextTemp = getValueByPath(data, "result.ws[].cw[0].w");}}`;
|
|
1591
|
+
|
|
1592
|
+
// you could use `if (data.result && data.result.pgs === "apd") { resultText += resultTextTemp;} resultTextTemp = data.result.ws.map(w => w.cw[0].w).join("");`
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
if (extractorTemplate) {
|
|
1596
|
+
try {
|
|
1597
|
+
|
|
1598
|
+
// Construct the execution function, passing in the context variables
|
|
1599
|
+
// Syntax Translation: Convert {{if}} to standard JS
|
|
1600
|
+
// Support: {{if}}, {{else}}, {{/if}}, and direct expressions {{...}}
|
|
1601
|
+
let jsScript = extractorTemplate
|
|
1602
|
+
.replace(/{{if\s+(.+?)}}/g, 'if ($1) {')
|
|
1603
|
+
.replace(/{{else}}/g, '} else {')
|
|
1604
|
+
.replace(/{{\/if}}/g, '}')
|
|
1605
|
+
.replace(/{{(.+?)}}/g, '$1');
|
|
1606
|
+
|
|
1607
|
+
/**
|
|
1608
|
+
* Sandbox Execution Environment
|
|
1609
|
+
* @param data - The source data (jsonData.data)
|
|
1610
|
+
* @param resultText - Confirmed text from previous turns
|
|
1611
|
+
* @param resultTextTemp - Temporary text from the current turn
|
|
1612
|
+
* @param getValueByPath - Helper for safe property access
|
|
1613
|
+
*/
|
|
1614
|
+
const resolver = new Function('data', 'resultText', 'resultTextTemp', 'getValueByPath', `
|
|
1615
|
+
try {
|
|
1616
|
+
${jsScript}
|
|
1617
|
+
} catch (e) {
|
|
1618
|
+
console.error("Voice Template Runtime Error:", e);
|
|
1619
|
+
}
|
|
1620
|
+
// Crucial: Return updated values to synchronize with component scope
|
|
1621
|
+
return { resultText, resultTextTemp };
|
|
1622
|
+
`);
|
|
1623
|
+
|
|
1624
|
+
// Execute and update local variables
|
|
1625
|
+
const updated = resolver(data, resultText, resultTextTemp, getValueByPath);
|
|
1626
|
+
resultText = updated.resultText;
|
|
1627
|
+
resultTextTemp = updated.resultTextTemp;
|
|
1628
|
+
|
|
1629
|
+
} catch (err) {
|
|
1630
|
+
console.error("--> [ERROR] Failed to parse property: [voiceResponseExtractor]");
|
|
1631
|
+
interruptVoiceInput();
|
|
1595
1632
|
}
|
|
1596
|
-
resultTextTemp = extractedStr;
|
|
1597
1633
|
}
|
|
1598
1634
|
|
|
1635
|
+
//
|
|
1599
1636
|
const currentFullText = resultText + resultTextTemp;
|
|
1600
1637
|
|
|
1601
1638
|
// Store to Ref for use by stopVoiceInput
|
|
@@ -1694,6 +1731,7 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
1694
1731
|
console.error('--> The voice input failed to start:', error);
|
|
1695
1732
|
config.onVoiceInputError?.(error as Error);
|
|
1696
1733
|
}
|
|
1734
|
+
|
|
1697
1735
|
};
|
|
1698
1736
|
|
|
1699
1737
|
const stopVoiceInput = async () => {
|
|
@@ -2029,24 +2067,36 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
2029
2067
|
if (enableVoiceInput) {
|
|
2030
2068
|
|
|
2031
2069
|
// Helper to stop recording safely
|
|
2032
|
-
const handleActionStop = (e: React.SyntheticEvent) => {
|
|
2033
|
-
// Prevent firing both Mouse and Touch events on some devices
|
|
2034
|
-
if (e.cancelable) e.preventDefault();
|
|
2035
|
-
|
|
2036
|
-
if (isVoiceInputActive) {
|
|
2037
|
-
stopVoiceInput();
|
|
2038
|
-
}
|
|
2039
|
-
};
|
|
2040
|
-
|
|
2041
2070
|
const handleActionStart = (e: React.SyntheticEvent) => {
|
|
2042
2071
|
if (loading) return;
|
|
2043
2072
|
if (e.cancelable) e.preventDefault();
|
|
2044
|
-
|
|
2073
|
+
|
|
2074
|
+
pressStartTimeRef.current = Date.now();
|
|
2075
|
+
|
|
2045
2076
|
if (!isVoiceInputActive) {
|
|
2046
2077
|
startVoiceInput();
|
|
2047
2078
|
}
|
|
2048
2079
|
};
|
|
2049
2080
|
|
|
2081
|
+
const handleActionStop = (e: React.SyntheticEvent) => {
|
|
2082
|
+
if (e.cancelable) e.preventDefault();
|
|
2083
|
+
|
|
2084
|
+
const duration = Date.now() - pressStartTimeRef.current;
|
|
2085
|
+
|
|
2086
|
+
// If the press time is extremely short (e.g. less than 100ms),
|
|
2087
|
+
// it means that it is a mistakeal touch or extremely fast
|
|
2088
|
+
// Add a small delay to execute to ensure that startVoiceInput has finished initializing
|
|
2089
|
+
if (duration < 150) { // > 50, < 160
|
|
2090
|
+
setTimeout(() => {
|
|
2091
|
+
stopVoiceInput();
|
|
2092
|
+
}, 100);
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
stopVoiceInput();
|
|
2097
|
+
|
|
2098
|
+
};
|
|
2099
|
+
|
|
2050
2100
|
return (
|
|
2051
2101
|
<div className={`${args().prefix || 'custom-'}chatbox-voice-input-btn-wrapper ${isVoiceInputActive ? 'active' : ''}`}>
|
|
2052
2102
|
{isVoiceInputActive ? <span></span> : null}
|
|
@@ -2054,15 +2104,13 @@ const Chatbox = (props: ChatboxProps) => {
|
|
|
2054
2104
|
// CSS touch-action is crucial here
|
|
2055
2105
|
style={!holdToTalk ? undefined : { touchAction: 'none', userSelect: 'none' }}
|
|
2056
2106
|
|
|
2057
|
-
// Mobile
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
onMouseUp={!holdToTalk ? undefined : handleActionStop}
|
|
2065
|
-
onMouseLeave={!holdToTalk ? undefined : handleActionStop} // Handle mouse dragging out of button
|
|
2107
|
+
// Mobile & Desktop
|
|
2108
|
+
// (using Pointer to avoid the stop problem caused by mixing Touch and Mouse)
|
|
2109
|
+
onPointerDown={holdToTalk ? handleActionStart : undefined}
|
|
2110
|
+
onPointerUp={holdToTalk ? handleActionStop : undefined}
|
|
2111
|
+
onPointerCancel={holdToTalk ? handleActionStop : undefined}
|
|
2112
|
+
onPointerLeave={holdToTalk ? handleActionStop : undefined}
|
|
2113
|
+
onContextMenu={(e) => e.preventDefault()}
|
|
2066
2114
|
onClick={(e: React.MouseEvent) => {
|
|
2067
2115
|
e.preventDefault();
|
|
2068
2116
|
e.stopPropagation();
|
|
@@ -171,7 +171,7 @@ export function extractHtmlTags(html: string): { processedHtml: string; placehol
|
|
|
171
171
|
};
|
|
172
172
|
|
|
173
173
|
|
|
174
|
-
export function toBoolean(val) {
|
|
174
|
+
export function toBoolean(val: any) {
|
|
175
175
|
if (typeof val === "boolean") return val;
|
|
176
176
|
if (typeof val === "string") {
|
|
177
177
|
return val.toLowerCase() === "true";
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"UIUX Lab","email":"uiuxlab@gmail.com","name":"funda-ui","version":"4.7.
|
|
1
|
+
{"author":"UIUX Lab","email":"uiuxlab@gmail.com","name":"funda-ui","version":"4.7.850","description":"React components using pure Bootstrap 5+ which does not contain any external style and script libraries.","repository":{"type":"git","url":"git+https://github.com/xizon/funda-ui.git"},"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"keywords":["bootstrap","react-bootstrap","react-components","components","components-react","react-bootstrap-components","react","funda-ui","fundaui","uikit","ui-kit","ui-components"],"bugs":{"url":"https://github.com/xizon/funda-ui/issues"},"homepage":"https://github.com/xizon/funda-ui#readme","main":"all.js","license":"MIT","dependencies":{"react":"^18.2.0","react-dom":"^18.2.0"},"types":"all.d.ts","publishConfig":{"directory":"lib"}}
|