dmed-voice-assistant 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.
- package/README.md +2 -0
- package/package.json +70 -0
- package/src/VoiceAssistant/index.js +45 -0
- package/src/VoiceAssistant/recognition.js +952 -0
- package/src/VoiceAssistant/recorder.js +575 -0
@@ -0,0 +1,952 @@
|
|
1
|
+
import { useState, useEffect, useRef } from "react";
|
2
|
+
import { Box, Typography, Divider, MenuItem, styled, Select, Dialog, Popover, Button } from "@mui/material";
|
3
|
+
import {
|
4
|
+
RecognitionIcon,
|
5
|
+
CloseIcon,
|
6
|
+
RocketIcon,
|
7
|
+
StartIcon,
|
8
|
+
DarkSettingIcon,
|
9
|
+
StopIcon,
|
10
|
+
HistoryIcon,
|
11
|
+
BookIcon,
|
12
|
+
RedirectIcon,
|
13
|
+
SmallPauseIcon,
|
14
|
+
RedStopIcon,
|
15
|
+
MicIcon,
|
16
|
+
LanguageIcon,
|
17
|
+
SmallCloseIcon,
|
18
|
+
CommandIcon
|
19
|
+
} from "../shared/svgs";
|
20
|
+
import RecognitionListItem from "../shared/RecognitionListItem";
|
21
|
+
|
22
|
+
const StyledSelect = styled(Select)(({ theme }) => ({
|
23
|
+
height: '41.4px',
|
24
|
+
border: '0.9px solid #D8DEE4',
|
25
|
+
borderRadius: '5px',
|
26
|
+
maxWidth: '375px',
|
27
|
+
'& .MuiOutlinedInput-notchedOutline': {
|
28
|
+
border: "none",
|
29
|
+
},
|
30
|
+
'& svg': {
|
31
|
+
fill: '#494A48'
|
32
|
+
},
|
33
|
+
}));
|
34
|
+
|
35
|
+
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
36
|
+
'& .MuiPaper-root': {
|
37
|
+
background: '#FBF3DE',
|
38
|
+
border: '1px solid #FFB90080',
|
39
|
+
borderRadius: '5px',
|
40
|
+
padding: '8px 10px'
|
41
|
+
},
|
42
|
+
}));
|
43
|
+
|
44
|
+
const StyledTypography = styled(Typography)(({ theme }) => ({
|
45
|
+
padding: '3px 6px',
|
46
|
+
background: '#FFB90033',
|
47
|
+
borderRadius: '5px',
|
48
|
+
fontWeight: '400 !important',
|
49
|
+
fontSize: '12px !important',
|
50
|
+
color: '#494A48 !important',
|
51
|
+
lineHeight: '15.59px'
|
52
|
+
}));
|
53
|
+
|
54
|
+
const Recognition = ({
|
55
|
+
mode = 'recorder',
|
56
|
+
recognitionHistoryList,
|
57
|
+
setMode,
|
58
|
+
onNewRecognitionEvent,
|
59
|
+
onRecognitionDataChange
|
60
|
+
}) => {
|
61
|
+
const [open, setOpen] = useState(false);
|
62
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
63
|
+
const openAnchorEl = Boolean(anchorEl);
|
64
|
+
const [isStarted, setIsStarted] = useState(false);
|
65
|
+
const [isSteup, setIsSetup] = useState(false);
|
66
|
+
const [selectedVoice, setSelectedVoice] = useState("");
|
67
|
+
const [voiceList, setVoiceList] = useState([]);
|
68
|
+
const languageList = ['Auto-Detect', 'English', 'Chinese (Simplified)'];
|
69
|
+
const [selectedLanguage, setSelectedLanguage] = useState("");
|
70
|
+
|
71
|
+
const recognitionRef = useRef(null);
|
72
|
+
const [result, setResult] = useState([]);
|
73
|
+
const [historyList, setHistoryList] = useState(recognitionHistoryList);
|
74
|
+
const [recordTime, setRecordTime] = useState(0);
|
75
|
+
const [intervalId, setIntervalId] = useState(null);
|
76
|
+
|
77
|
+
const handleClickOpen = () => {
|
78
|
+
setOpen(true);
|
79
|
+
};
|
80
|
+
const handleClose = () => {
|
81
|
+
setOpen(false);
|
82
|
+
};
|
83
|
+
|
84
|
+
const handlePopoverOpen = (event) => {
|
85
|
+
setAnchorEl(event.currentTarget);
|
86
|
+
};
|
87
|
+
|
88
|
+
const handlePopoverClose = () => {
|
89
|
+
setAnchorEl(null);
|
90
|
+
};
|
91
|
+
|
92
|
+
const handleModeChange = () => {
|
93
|
+
if(mode === "recorder") {
|
94
|
+
setMode("recognition");
|
95
|
+
} else if(mode === "recognition") {
|
96
|
+
setMode("recorder");
|
97
|
+
}
|
98
|
+
};
|
99
|
+
|
100
|
+
const handleStartChange = () => {
|
101
|
+
if(isSteup) return;
|
102
|
+
|
103
|
+
if(!isStarted) {
|
104
|
+
setIsStarted(!isStarted);
|
105
|
+
startRecording();
|
106
|
+
} else {
|
107
|
+
setIsStarted(false);
|
108
|
+
stopRecording();
|
109
|
+
}
|
110
|
+
};
|
111
|
+
|
112
|
+
const handleSetupChange = () => {
|
113
|
+
setIsSetup(!isSteup);
|
114
|
+
};
|
115
|
+
|
116
|
+
const handleVoiceChange = (event) => {
|
117
|
+
setSelectedVoice(event.target.value);
|
118
|
+
};
|
119
|
+
|
120
|
+
const handleLanguageChange = (event) => {
|
121
|
+
setSelectedLanguage(event.target.value);
|
122
|
+
};
|
123
|
+
|
124
|
+
const startRecording = () => {
|
125
|
+
if (recognitionRef.current) {
|
126
|
+
setResult([]);
|
127
|
+
setRecordTime(0);
|
128
|
+
const id = setInterval(async () => {
|
129
|
+
setRecordTime((prevCount) => prevCount + 1);
|
130
|
+
}, 1000);
|
131
|
+
setIntervalId(id);
|
132
|
+
recognitionRef.current.start();
|
133
|
+
}
|
134
|
+
};
|
135
|
+
|
136
|
+
const stopRecording = () => {
|
137
|
+
if (recognitionRef.current) {
|
138
|
+
recognitionRef.current.stop();
|
139
|
+
clearInterval(intervalId);
|
140
|
+
let temp = [...historyList];
|
141
|
+
const newData = {
|
142
|
+
result,
|
143
|
+
date: new Date()
|
144
|
+
}
|
145
|
+
temp.push(newData);
|
146
|
+
setHistoryList(temp);
|
147
|
+
|
148
|
+
if(onNewRecognitionEvent) {
|
149
|
+
onNewRecognitionEvent(newData);
|
150
|
+
}
|
151
|
+
if(onRecognitionDataChange) {
|
152
|
+
onRecognitionDataChange(temp);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
};
|
156
|
+
|
157
|
+
const startSpeechRecognition = () => {
|
158
|
+
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
159
|
+
|
160
|
+
if (SpeechRecognition) {
|
161
|
+
const recognition = new SpeechRecognition();
|
162
|
+
recognition.continuous = true;
|
163
|
+
recognition.interimResults = true;
|
164
|
+
recognition.lang = 'en-US';
|
165
|
+
|
166
|
+
recognition.onstart = () => {
|
167
|
+
console.log('Recording started');
|
168
|
+
};
|
169
|
+
|
170
|
+
recognition.onresult = (event) => {
|
171
|
+
setResult((prevTranscript) => {
|
172
|
+
let updatedTranscript = [...prevTranscript];
|
173
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
174
|
+
if (event.results[i].isFinal) {
|
175
|
+
const resultArr = event.results[i][0].transcript.split(' ').filter(word => word.trim() !== '');
|
176
|
+
updatedTranscript = updatedTranscript.concat(resultArr);
|
177
|
+
}
|
178
|
+
}
|
179
|
+
return updatedTranscript;
|
180
|
+
});
|
181
|
+
};
|
182
|
+
|
183
|
+
recognition.onerror = (event) => {
|
184
|
+
console.error('Speech recognition error:', event.error);
|
185
|
+
};
|
186
|
+
|
187
|
+
recognition.onend = () => {
|
188
|
+
setIsStarted(false);
|
189
|
+
clearInterval(intervalId);
|
190
|
+
console.log('Speech recognition ended');
|
191
|
+
};
|
192
|
+
|
193
|
+
recognitionRef.current = recognition;
|
194
|
+
} else {
|
195
|
+
console.error('Speech recognition not supported');
|
196
|
+
}
|
197
|
+
};
|
198
|
+
|
199
|
+
useEffect(() => {
|
200
|
+
const fetchAudioInputDevices = async () => {
|
201
|
+
try {
|
202
|
+
await navigator.mediaDevices.getUserMedia({ audio: true });
|
203
|
+
const devices = await navigator.mediaDevices.enumerateDevices();
|
204
|
+
|
205
|
+
// Filter only audio input devices
|
206
|
+
const audioInputs = devices.filter(device => device.kind === "audioinput");
|
207
|
+
let temp = ['Auto-Detect'];
|
208
|
+
audioInputs.forEach(device => {
|
209
|
+
temp.push(device.label);
|
210
|
+
});
|
211
|
+
setVoiceList(temp);
|
212
|
+
} catch (error) {
|
213
|
+
console.error("Error accessing audio devices:", error);
|
214
|
+
}
|
215
|
+
};
|
216
|
+
|
217
|
+
startSpeechRecognition();
|
218
|
+
fetchAudioInputDevices();
|
219
|
+
}, []);
|
220
|
+
|
221
|
+
return (
|
222
|
+
<Box
|
223
|
+
className="bg-white rounded-[5px] p-[20px] w-[440px]"
|
224
|
+
sx={{
|
225
|
+
border: '1px solid #D8DEE4'
|
226
|
+
}}
|
227
|
+
>
|
228
|
+
<Box className="flex justify-between">
|
229
|
+
<Box className="flex items-center space-x-2">
|
230
|
+
<Typography className="!font-[600] !text-[24px]" color="#494A48">
|
231
|
+
Voice assistant
|
232
|
+
</Typography>
|
233
|
+
<Box
|
234
|
+
className="flex items-center space-x-1 px-[10px] py-[3px] h-[24px] bg-[#006FFF4D] rounded-[89.1px] cursor-pointer"
|
235
|
+
sx={{
|
236
|
+
border: '0.9px solid #006FFFB2',
|
237
|
+
}}
|
238
|
+
onClick={handleModeChange}
|
239
|
+
>
|
240
|
+
<Box>
|
241
|
+
<RecognitionIcon />
|
242
|
+
</Box>
|
243
|
+
<Typography className="!font-[600] !text-[12px]" color="#494A48">
|
244
|
+
Recognition mode
|
245
|
+
</Typography>
|
246
|
+
</Box>
|
247
|
+
</Box>
|
248
|
+
<Box className="px-[10px] py-[8px] cursor-pointer">
|
249
|
+
<CloseIcon />
|
250
|
+
</Box>
|
251
|
+
</Box>
|
252
|
+
|
253
|
+
<Box className="flex items-center justify-between py-[4.5px] mt-1">
|
254
|
+
<Box className="flex space-x-1">
|
255
|
+
<Box>
|
256
|
+
<RocketIcon />
|
257
|
+
</Box>
|
258
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
259
|
+
Real-time
|
260
|
+
</Typography>
|
261
|
+
</Box>
|
262
|
+
|
263
|
+
<Box className="flex space-x-1">
|
264
|
+
{
|
265
|
+
!isStarted ?
|
266
|
+
<Box
|
267
|
+
className="flex items-center space-x-1 cursor-pointer rounded-[5px] px-[9px] h-[32.4px] bg-white"
|
268
|
+
sx={{
|
269
|
+
'&:hover': {
|
270
|
+
boxShadow: '0px 0px 5px 1px #44C63380',
|
271
|
+
cursor: isSteup && 'not-allowed'
|
272
|
+
}
|
273
|
+
}}
|
274
|
+
onClick={handleStartChange}
|
275
|
+
>
|
276
|
+
<Box>
|
277
|
+
<StartIcon />
|
278
|
+
</Box>
|
279
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
280
|
+
Start
|
281
|
+
</Typography>
|
282
|
+
</Box>
|
283
|
+
:
|
284
|
+
<Box
|
285
|
+
className="flex items-center justify-between space-x-4 cursor-pointer rounded-[5px] px-[9px] h-[32.4px] bg-white"
|
286
|
+
sx={{
|
287
|
+
border: "0.9px solid #F3151580",
|
288
|
+
'&:hover': {
|
289
|
+
boxShadow: '0px 0px 5px 1px #F3151580'
|
290
|
+
}
|
291
|
+
}}
|
292
|
+
onClick={handleStartChange}
|
293
|
+
>
|
294
|
+
<Box className="flex items-center space-x-1">
|
295
|
+
<Box>
|
296
|
+
<SmallPauseIcon />
|
297
|
+
</Box>
|
298
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
299
|
+
Stop
|
300
|
+
</Typography>
|
301
|
+
</Box>
|
302
|
+
<Box className="flex items-baseline">
|
303
|
+
<Typography
|
304
|
+
className="!font-[600] !text-[20px]"
|
305
|
+
color="#494847"
|
306
|
+
sx={{ fontFamily: "Afacad !important" }}
|
307
|
+
>
|
308
|
+
{recordTime}
|
309
|
+
</Typography>
|
310
|
+
<Typography
|
311
|
+
className="!font-400 !text-[16px]"
|
312
|
+
color="#797974"
|
313
|
+
sx={{ fontFamily: "Afacad !important" }}
|
314
|
+
>
|
315
|
+
s
|
316
|
+
</Typography>
|
317
|
+
</Box>
|
318
|
+
</Box>
|
319
|
+
}
|
320
|
+
<Box
|
321
|
+
className="flex items-center space-x-1 cursor-pointer rounded-[5px] px-[9px] h-[32.4px] bg-white"
|
322
|
+
sx={{
|
323
|
+
'&:hover': {
|
324
|
+
boxShadow: !isStarted ? '0px 0px 5px 1px #494A4880' : 'none',
|
325
|
+
cursor: isStarted && "not-allowed"
|
326
|
+
}
|
327
|
+
}}
|
328
|
+
onClick={handleSetupChange}
|
329
|
+
>
|
330
|
+
<Box>
|
331
|
+
<DarkSettingIcon fill={isStarted ? "#494A484D" : "#494A48" } />
|
332
|
+
</Box>
|
333
|
+
<Typography className="!font-400 !text-[14px]" color={isStarted ? "#494A484D" : "#494A48"}>
|
334
|
+
Setup
|
335
|
+
</Typography>
|
336
|
+
</Box>
|
337
|
+
</Box>
|
338
|
+
</Box>
|
339
|
+
|
340
|
+
{
|
341
|
+
!isSteup ?
|
342
|
+
<>
|
343
|
+
<Box className="rounded-[5px] mt-1" sx={{ border: '1px solid #D8DEE4' }}>
|
344
|
+
<Box className="flex items-center justify-between p-[4.5px]">
|
345
|
+
<Typography className="px-[4.5px] py-[4px] !font-400 !text-[12px]" color="#494A48">
|
346
|
+
{!isStarted ? '-' : result.length} words
|
347
|
+
</Typography>
|
348
|
+
<Box className="flex items-center space-x-1 px-[9px]">
|
349
|
+
<Box>
|
350
|
+
{!isStarted ? <StopIcon /> : <RedStopIcon />}
|
351
|
+
</Box>
|
352
|
+
<Typography className="!font-[600] !text-[14px]" color="#494A48">
|
353
|
+
{!isStarted ? 'Stop' : 'REC'}
|
354
|
+
</Typography>
|
355
|
+
</Box>
|
356
|
+
</Box>
|
357
|
+
|
358
|
+
{
|
359
|
+
isStarted &&
|
360
|
+
<Box
|
361
|
+
className="flex items-center flex-wrap space-x-1 mt-1 p-[9px]"
|
362
|
+
sx={{ maxWidth: "387px", overflow: "hidden" }}
|
363
|
+
>
|
364
|
+
{
|
365
|
+
result.map((item, index) => {
|
366
|
+
return (
|
367
|
+
<Box className="flex items-center" key={index}>
|
368
|
+
<Typography
|
369
|
+
className="!font-400 !text-[16px]" color="#494A48"
|
370
|
+
sx={{ fontFamily: "Space Grotesk !important" }}
|
371
|
+
>
|
372
|
+
{item}
|
373
|
+
</Typography>
|
374
|
+
{
|
375
|
+
index !== result.length - 1 &&
|
376
|
+
<Typography
|
377
|
+
className="!font-400 !text-[16px]" color="#494A4880"
|
378
|
+
sx={{ fontFamily: "Fira Sans !important" }}
|
379
|
+
>
|
380
|
+
,
|
381
|
+
</Typography>
|
382
|
+
}
|
383
|
+
</Box>
|
384
|
+
)
|
385
|
+
})
|
386
|
+
}
|
387
|
+
</Box>
|
388
|
+
}
|
389
|
+
</Box>
|
390
|
+
|
391
|
+
<Box className="mt-1">
|
392
|
+
<Box className="flex items-center justify-between py-[4.5px]">
|
393
|
+
<Box className="flex space-x-1">
|
394
|
+
<Box>
|
395
|
+
<HistoryIcon />
|
396
|
+
</Box>
|
397
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
398
|
+
History
|
399
|
+
</Typography>
|
400
|
+
</Box>
|
401
|
+
|
402
|
+
<Button
|
403
|
+
disabled={isStarted}
|
404
|
+
color="#494A48"
|
405
|
+
sx={{
|
406
|
+
border: '0.9px solid #D8DEE4',
|
407
|
+
fontSize: '14px !important',
|
408
|
+
fontWeight: '400 !important',
|
409
|
+
fontFamily: 'Reddit Sans !important',
|
410
|
+
height: '32px !important',
|
411
|
+
textTransform: "none"
|
412
|
+
}}
|
413
|
+
onClick={handlePopoverOpen}
|
414
|
+
>
|
415
|
+
02.July.2024
|
416
|
+
</Button>
|
417
|
+
</Box>
|
418
|
+
|
419
|
+
<Box
|
420
|
+
className="flex flex-col scrollableBox mt-1"
|
421
|
+
sx={{
|
422
|
+
maxHeight: "160px",
|
423
|
+
}}
|
424
|
+
>
|
425
|
+
{
|
426
|
+
historyList.map((history, index) => {
|
427
|
+
return (
|
428
|
+
<RecognitionListItem
|
429
|
+
data={history}
|
430
|
+
key={index}
|
431
|
+
/>
|
432
|
+
)
|
433
|
+
})
|
434
|
+
}
|
435
|
+
</Box>
|
436
|
+
|
437
|
+
<Box
|
438
|
+
className="flex items-center justify-between rounded-[5px] bg-[#FBF3DE80] px-[5px] py-[8px] mt-1 cursor-pointer"
|
439
|
+
sx={{
|
440
|
+
border: '1px solid #FFB90080',
|
441
|
+
}}
|
442
|
+
onClick={handleClickOpen}
|
443
|
+
>
|
444
|
+
<Box className="flex items-center space-x-1">
|
445
|
+
<Box>
|
446
|
+
<BookIcon />
|
447
|
+
</Box>
|
448
|
+
<Typography className="!font-[600] !text-[14px]" color="#494A48">
|
449
|
+
Voice command instructions
|
450
|
+
</Typography>
|
451
|
+
</Box>
|
452
|
+
<Box>
|
453
|
+
<RedirectIcon />
|
454
|
+
</Box>
|
455
|
+
</Box>
|
456
|
+
</Box>
|
457
|
+
</>
|
458
|
+
:
|
459
|
+
<>
|
460
|
+
<Divider />
|
461
|
+
|
462
|
+
<Box className="flex space-x-1 w-full mt-2">
|
463
|
+
<Box>
|
464
|
+
<MicIcon fill="#494A48" />
|
465
|
+
</Box>
|
466
|
+
<Box className="flex-1">
|
467
|
+
<Typography className="!font-[600] !text-[16px]" color="#494A48">
|
468
|
+
Voice
|
469
|
+
</Typography>
|
470
|
+
<Typography className="!font-400 !text-[14px] pt-1" color="#494A48">
|
471
|
+
Input device
|
472
|
+
</Typography>
|
473
|
+
<StyledSelect
|
474
|
+
className="mt-1"
|
475
|
+
fullWidth
|
476
|
+
displayEmpty
|
477
|
+
value={selectedVoice}
|
478
|
+
onChange={handleVoiceChange}
|
479
|
+
renderValue={(selected) => {
|
480
|
+
if (selected === "") {
|
481
|
+
return <span style={{
|
482
|
+
fontSize: '12.6px',
|
483
|
+
fontWeight: '400',
|
484
|
+
lineHeight: '25.2px',
|
485
|
+
color: '#494A4899' }}>Auto-Detect</span>;
|
486
|
+
}
|
487
|
+
return <span style={{
|
488
|
+
fontSize: '12.6px',
|
489
|
+
fontWeight: '400',
|
490
|
+
lineHeight: '25.2px',
|
491
|
+
color: '#494A4899' }}>{voiceList[selected]}</span>;
|
492
|
+
}}
|
493
|
+
MenuProps={{
|
494
|
+
PaperProps: {
|
495
|
+
sx: {
|
496
|
+
marginTop: "5px",
|
497
|
+
border: '0.9px solid #D8DEE4',
|
498
|
+
padding: '5px',
|
499
|
+
'& .MuiList-root': {
|
500
|
+
padding: "unset"
|
501
|
+
},
|
502
|
+
'& .MuiMenuItem-root': {
|
503
|
+
padding: "7.2px 9px",
|
504
|
+
color: '#494A4899',
|
505
|
+
fontFamily: 'Reddit Sans',
|
506
|
+
fontSize: '12.6px',
|
507
|
+
fontWeight: '400',
|
508
|
+
lineHeight: '25.2px'
|
509
|
+
}
|
510
|
+
},
|
511
|
+
},
|
512
|
+
}}
|
513
|
+
>
|
514
|
+
{
|
515
|
+
voiceList.map((device, index) => {
|
516
|
+
return (
|
517
|
+
<MenuItem value={index} key={index}>{device}</MenuItem>
|
518
|
+
)
|
519
|
+
})
|
520
|
+
}
|
521
|
+
</StyledSelect>
|
522
|
+
</Box>
|
523
|
+
</Box>
|
524
|
+
|
525
|
+
<Box className="flex space-x-1 w-full mt-2">
|
526
|
+
<Box>
|
527
|
+
<LanguageIcon fill="#494A48" />
|
528
|
+
</Box>
|
529
|
+
<Box className="flex-1">
|
530
|
+
<Typography className="!font-[600] !text-[16px]" color="#494A48">
|
531
|
+
Language
|
532
|
+
</Typography>
|
533
|
+
<Typography className="!font-400 !text-[14px] pt-1" color="#494A48">
|
534
|
+
Prefer language
|
535
|
+
</Typography>
|
536
|
+
<StyledSelect
|
537
|
+
className="mt-1"
|
538
|
+
fullWidth
|
539
|
+
displayEmpty
|
540
|
+
value={selectedLanguage}
|
541
|
+
onChange={handleLanguageChange}
|
542
|
+
renderValue={(selected) => {
|
543
|
+
if (selected === "") {
|
544
|
+
return <span style={{
|
545
|
+
fontSize: '12.6px',
|
546
|
+
fontWeight: '400',
|
547
|
+
lineHeight: '25.2px',
|
548
|
+
color: '#494A4899' }}>Auto-Detect</span>;
|
549
|
+
}
|
550
|
+
return <span style={{
|
551
|
+
fontSize: '12.6px',
|
552
|
+
fontWeight: '400',
|
553
|
+
lineHeight: '25.2px',
|
554
|
+
color: '#494A4899' }}>{languageList[selected]}</span>;
|
555
|
+
}}
|
556
|
+
MenuProps={{
|
557
|
+
PaperProps: {
|
558
|
+
sx: {
|
559
|
+
marginTop: "5px",
|
560
|
+
border: '0.9px solid #D8DEE4',
|
561
|
+
padding: '5px',
|
562
|
+
'& .MuiList-root': {
|
563
|
+
padding: "unset"
|
564
|
+
},
|
565
|
+
'& .MuiMenuItem-root': {
|
566
|
+
padding: "7.2px 9px",
|
567
|
+
color: '#494A4899',
|
568
|
+
fontFamily: 'Reddit Sans',
|
569
|
+
fontSize: '12.6px',
|
570
|
+
fontWeight: '400',
|
571
|
+
lineHeight: '25.2px'
|
572
|
+
}
|
573
|
+
},
|
574
|
+
},
|
575
|
+
}}
|
576
|
+
>
|
577
|
+
<MenuItem value={0}>Auto-Detect</MenuItem>
|
578
|
+
<MenuItem value={1}>English</MenuItem>
|
579
|
+
<MenuItem value={2}>Chinese (Simplified)</MenuItem>
|
580
|
+
</StyledSelect>
|
581
|
+
</Box>
|
582
|
+
</Box>
|
583
|
+
</>
|
584
|
+
}
|
585
|
+
|
586
|
+
<StyledDialog
|
587
|
+
onClose={handleClose}
|
588
|
+
aria-labelledby="customized-dialog-title"
|
589
|
+
open={open}
|
590
|
+
>
|
591
|
+
<Box className="flex items-center justify-between py-[8px]">
|
592
|
+
<Box className="flex items-center space-x-1">
|
593
|
+
<Box>
|
594
|
+
<BookIcon />
|
595
|
+
</Box>
|
596
|
+
<Typography className="!font-[600] !text-[14px]" color="#494A48">
|
597
|
+
Voice command instructions
|
598
|
+
</Typography>
|
599
|
+
</Box>
|
600
|
+
<Box className="cursor-pointer" onClick={handleClose}>
|
601
|
+
<SmallCloseIcon />
|
602
|
+
</Box>
|
603
|
+
</Box>
|
604
|
+
|
605
|
+
<Box className="py-[8px]">
|
606
|
+
<Typography className="!font-[600] !text-[12px]" color="#494A48">
|
607
|
+
Tooth position (FDI)
|
608
|
+
</Typography>
|
609
|
+
<Box className="flex items-center py-[5px]">
|
610
|
+
<StyledTypography>
|
611
|
+
Upper
|
612
|
+
</StyledTypography>
|
613
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
614
|
+
/
|
615
|
+
</Typography>
|
616
|
+
<StyledTypography>
|
617
|
+
Lower
|
618
|
+
</StyledTypography>
|
619
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
620
|
+
+
|
621
|
+
</Typography>
|
622
|
+
<StyledTypography>
|
623
|
+
Left
|
624
|
+
</StyledTypography>
|
625
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
626
|
+
/
|
627
|
+
</Typography>
|
628
|
+
<StyledTypography>
|
629
|
+
Right
|
630
|
+
</StyledTypography>
|
631
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
632
|
+
+
|
633
|
+
</Typography>
|
634
|
+
<StyledTypography>
|
635
|
+
number
|
636
|
+
</StyledTypography>
|
637
|
+
<Typography className="!font-400 !text-[12px] pl-2" color="#494A48">
|
638
|
+
Tooth position (FDI)
|
639
|
+
</Typography>
|
640
|
+
</Box>
|
641
|
+
<Box className="flex items-center pt-[5px]">
|
642
|
+
<StyledTypography>
|
643
|
+
Jump
|
644
|
+
</StyledTypography>
|
645
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
646
|
+
+
|
647
|
+
</Typography>
|
648
|
+
<StyledTypography>
|
649
|
+
number
|
650
|
+
</StyledTypography>
|
651
|
+
<Typography className="!font-400 !text-[12px] pl-2" color="#494A48">
|
652
|
+
Jump to tooth(FDI)
|
653
|
+
</Typography>
|
654
|
+
</Box>
|
655
|
+
</Box>
|
656
|
+
|
657
|
+
<Box className="pb-[8px]">
|
658
|
+
<Typography className="!font-[600] !text-[12px]" color="#494A48">
|
659
|
+
Terminology
|
660
|
+
</Typography>
|
661
|
+
<Box className="flex items-center space-x-5 py-[5px]">
|
662
|
+
<Box className="flex items-center space-x-2">
|
663
|
+
<StyledTypography>
|
664
|
+
Mobility
|
665
|
+
</StyledTypography>
|
666
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
667
|
+
Mobility
|
668
|
+
</Typography>
|
669
|
+
</Box>
|
670
|
+
<Box className="flex items-center space-x-2">
|
671
|
+
<StyledTypography>
|
672
|
+
Furcation
|
673
|
+
</StyledTypography>
|
674
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
675
|
+
Furcation
|
676
|
+
</Typography>
|
677
|
+
</Box>
|
678
|
+
</Box>
|
679
|
+
<Box className="flex items-center space-x-5 py-[5px]">
|
680
|
+
<Box className="flex items-center space-x-2">
|
681
|
+
<StyledTypography>
|
682
|
+
Bleeding
|
683
|
+
</StyledTypography>
|
684
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
685
|
+
Bleeding of point
|
686
|
+
</Typography>
|
687
|
+
</Box>
|
688
|
+
<Box className="flex items-center space-x-2">
|
689
|
+
<StyledTypography>
|
690
|
+
Plaque
|
691
|
+
</StyledTypography>
|
692
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
693
|
+
Plaque of point
|
694
|
+
</Typography>
|
695
|
+
</Box>
|
696
|
+
</Box>
|
697
|
+
<Box className="flex items-center space-x-5 py-[5px]">
|
698
|
+
<Box className="flex items-center space-x-2">
|
699
|
+
<StyledTypography>
|
700
|
+
Buccal
|
701
|
+
</StyledTypography>
|
702
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
703
|
+
Buccal
|
704
|
+
</Typography>
|
705
|
+
</Box>
|
706
|
+
<Box className="flex items-center space-x-2">
|
707
|
+
<StyledTypography>
|
708
|
+
Lingual
|
709
|
+
</StyledTypography>
|
710
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
711
|
+
Lingual
|
712
|
+
</Typography>
|
713
|
+
</Box>
|
714
|
+
</Box>
|
715
|
+
</Box>
|
716
|
+
|
717
|
+
<Box className="pb-[8px]">
|
718
|
+
<Typography className="!font-[600] !text-[12px]" color="#494A48">
|
719
|
+
Scenario 1
|
720
|
+
</Typography>
|
721
|
+
<Typography className="!font-[600] !text-[12px] px-[10px] pt-[5px]" color="#494A48">
|
722
|
+
Measure the full mouth pocket depth, then bleeding and plaque
|
723
|
+
</Typography>
|
724
|
+
<Box className="flex items-center space-x-2 px-[10px] py-[5px]">
|
725
|
+
<Box className="flex items-center space-x-1">
|
726
|
+
<Box>
|
727
|
+
<CommandIcon />
|
728
|
+
</Box>
|
729
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
730
|
+
Command:
|
731
|
+
</Typography>
|
732
|
+
</Box>
|
733
|
+
<Box className="flex items-center">
|
734
|
+
<StyledTypography>
|
735
|
+
One
|
736
|
+
</StyledTypography>
|
737
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
738
|
+
+
|
739
|
+
</Typography>
|
740
|
+
<StyledTypography>
|
741
|
+
Bleeding
|
742
|
+
</StyledTypography>
|
743
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
744
|
+
+
|
745
|
+
</Typography>
|
746
|
+
<StyledTypography>
|
747
|
+
Plaque
|
748
|
+
</StyledTypography>
|
749
|
+
</Box>
|
750
|
+
</Box>
|
751
|
+
</Box>
|
752
|
+
|
753
|
+
<Box className="pb-[8px]">
|
754
|
+
<Typography className="!font-[600] !text-[12px]" color="#494A48">
|
755
|
+
Scenario 2
|
756
|
+
</Typography>
|
757
|
+
<Typography className="!font-[600] !text-[12px] px-[10px] pt-[5px]" color="#494A48">
|
758
|
+
Jump to specific tooth position and check implant, crown, missing
|
759
|
+
</Typography>
|
760
|
+
<Box className="flex items-center space-x-2 px-[10px] py-[5px]">
|
761
|
+
<Box className="flex items-center space-x-1">
|
762
|
+
<Box>
|
763
|
+
<CommandIcon />
|
764
|
+
</Box>
|
765
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
766
|
+
Command:
|
767
|
+
</Typography>
|
768
|
+
</Box>
|
769
|
+
<Box className="flex items-center">
|
770
|
+
<StyledTypography>
|
771
|
+
Jump
|
772
|
+
</StyledTypography>
|
773
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
774
|
+
+
|
775
|
+
</Typography>
|
776
|
+
<StyledTypography>
|
777
|
+
Twenty-four
|
778
|
+
</StyledTypography>
|
779
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
780
|
+
+
|
781
|
+
</Typography>
|
782
|
+
<StyledTypography>
|
783
|
+
Implant
|
784
|
+
</StyledTypography>
|
785
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
786
|
+
/
|
787
|
+
</Typography>
|
788
|
+
<StyledTypography>
|
789
|
+
Crown
|
790
|
+
</StyledTypography>
|
791
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
792
|
+
/
|
793
|
+
</Typography>
|
794
|
+
<StyledTypography>
|
795
|
+
Missing
|
796
|
+
</StyledTypography>
|
797
|
+
</Box>
|
798
|
+
</Box>
|
799
|
+
</Box>
|
800
|
+
|
801
|
+
<Box className="pb-[8px]">
|
802
|
+
<Typography className="!font-[600] !text-[12px]" color="#494A48">
|
803
|
+
Scenario 3
|
804
|
+
</Typography>
|
805
|
+
<Typography className="!font-[600] !text-[12px] px-[10px] pt-[5px]" color="#494A48">
|
806
|
+
Jump to specific tooth position and check buccal bleeding
|
807
|
+
</Typography>
|
808
|
+
<Box className="flex items-center space-x-2 px-[10px] py-[5px]">
|
809
|
+
<Box className="flex items-center space-x-1">
|
810
|
+
<Box>
|
811
|
+
<CommandIcon />
|
812
|
+
</Box>
|
813
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
814
|
+
Command:
|
815
|
+
</Typography>
|
816
|
+
</Box>
|
817
|
+
<Box className="flex items-center">
|
818
|
+
<StyledTypography>
|
819
|
+
Jump
|
820
|
+
</StyledTypography>
|
821
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
822
|
+
+
|
823
|
+
</Typography>
|
824
|
+
<StyledTypography>
|
825
|
+
Seventeen
|
826
|
+
</StyledTypography>
|
827
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
828
|
+
+
|
829
|
+
</Typography>
|
830
|
+
<StyledTypography>
|
831
|
+
Buccal
|
832
|
+
</StyledTypography>
|
833
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
834
|
+
+
|
835
|
+
</Typography>
|
836
|
+
<StyledTypography>
|
837
|
+
Bleeding
|
838
|
+
</StyledTypography>
|
839
|
+
</Box>
|
840
|
+
</Box>
|
841
|
+
<Typography className="!font-[600] !text-[12px] px-[10px] pt-[5px]" color="#494A48">
|
842
|
+
Jump to specific tooth position and check mobility II
|
843
|
+
</Typography>
|
844
|
+
<Box className="flex items-center space-x-2 px-[10px] py-[5px]">
|
845
|
+
<Box className="flex items-center space-x-1">
|
846
|
+
<Box>
|
847
|
+
<CommandIcon />
|
848
|
+
</Box>
|
849
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
850
|
+
Command:
|
851
|
+
</Typography>
|
852
|
+
</Box>
|
853
|
+
<Box className="flex items-center">
|
854
|
+
<StyledTypography>
|
855
|
+
Jump
|
856
|
+
</StyledTypography>
|
857
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
858
|
+
+
|
859
|
+
</Typography>
|
860
|
+
<StyledTypography>
|
861
|
+
Thirty-three
|
862
|
+
</StyledTypography>
|
863
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
864
|
+
+
|
865
|
+
</Typography>
|
866
|
+
<StyledTypography>
|
867
|
+
Lingual
|
868
|
+
</StyledTypography>
|
869
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
870
|
+
+
|
871
|
+
</Typography>
|
872
|
+
<StyledTypography>
|
873
|
+
Mobility
|
874
|
+
</StyledTypography>
|
875
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
876
|
+
+
|
877
|
+
</Typography>
|
878
|
+
<StyledTypography>
|
879
|
+
Two
|
880
|
+
</StyledTypography>
|
881
|
+
</Box>
|
882
|
+
</Box>
|
883
|
+
</Box>
|
884
|
+
</StyledDialog>
|
885
|
+
|
886
|
+
<Popover
|
887
|
+
open={openAnchorEl}
|
888
|
+
anchorEl={anchorEl}
|
889
|
+
onClose={handlePopoverClose}
|
890
|
+
anchorOrigin={{
|
891
|
+
vertical: 'bottom',
|
892
|
+
horizontal: 'right',
|
893
|
+
}}
|
894
|
+
transformOrigin={{
|
895
|
+
vertical: 'top',
|
896
|
+
horizontal: 'right',
|
897
|
+
}}
|
898
|
+
sx={{
|
899
|
+
'& .MuiPaper-root': {
|
900
|
+
boxShadow: 'none',
|
901
|
+
border: '1px solid #D8DEE4',
|
902
|
+
borderRadius: '5px',
|
903
|
+
marginTop: '5px'
|
904
|
+
}
|
905
|
+
}}
|
906
|
+
>
|
907
|
+
<Box className="px-[5px] py-[2.5px] w-[200px]">
|
908
|
+
<Box className="flex items-center px-[10px] py-[3px]">
|
909
|
+
<Typography className="!font-bold !text-[14px]" color="#494A48B2">
|
910
|
+
Data
|
911
|
+
</Typography>
|
912
|
+
</Box>
|
913
|
+
<Box
|
914
|
+
className="flex items-center justify-between rounded-[5px] px-[10px] py-[3px] cursor-pointer"
|
915
|
+
sx={{
|
916
|
+
'&:hover': {
|
917
|
+
background: '#F6F6F6'
|
918
|
+
}
|
919
|
+
}}
|
920
|
+
>
|
921
|
+
<Box className="flex items-center space-x-1">
|
922
|
+
<Typography className="!font-bold !text-[14px]" color="#494A48">
|
923
|
+
7
|
924
|
+
</Typography>
|
925
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
926
|
+
words
|
927
|
+
</Typography>
|
928
|
+
</Box>
|
929
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
930
|
+
02.July.2024
|
931
|
+
</Typography>
|
932
|
+
</Box>
|
933
|
+
<Box className="flex items-center justify-between px-[10px] py-[3px]">
|
934
|
+
<Box className="flex items-center space-x-1">
|
935
|
+
<Typography className="!font-bold !text-[14px]" color="#494A48">
|
936
|
+
7
|
937
|
+
</Typography>
|
938
|
+
<Typography className="!font-400 !text-[12px]" color="#494A48">
|
939
|
+
words
|
940
|
+
</Typography>
|
941
|
+
</Box>
|
942
|
+
<Typography className="!font-400 !text-[14px]" color="#494A48">
|
943
|
+
02.July.2024
|
944
|
+
</Typography>
|
945
|
+
</Box>
|
946
|
+
</Box>
|
947
|
+
</Popover>
|
948
|
+
</Box>
|
949
|
+
);
|
950
|
+
};
|
951
|
+
|
952
|
+
export default Recognition;
|