@flozy/editor 4.0.1 → 4.0.2
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/dist/Editor/ChatEditor.js +3 -19
- package/dist/Editor/CommonEditor.js +181 -109
- package/dist/Editor/Editor.css +31 -3
- package/dist/Editor/Elements/AI/AIInput.js +31 -33
- package/dist/Editor/Elements/AI/CustomSelect.js +19 -12
- package/dist/Editor/Elements/AI/PopoverAIInput.js +73 -97
- package/dist/Editor/Elements/AI/Styles.js +2 -2
- package/dist/Editor/Elements/AI/VoiceToText/AudioWave.js +73 -0
- package/dist/Editor/Elements/AI/VoiceToText/index.js +184 -0
- package/dist/Editor/Elements/AI/VoiceToText/style.js +40 -0
- package/dist/Editor/Elements/AI/helper.js +5 -3
- package/dist/Editor/Elements/Accordion/Accordion.js +1 -1
- package/dist/Editor/Elements/Accordion/AccordionSummary.js +5 -10
- package/dist/Editor/Elements/AppHeader/AppHeader.js +26 -4
- package/dist/Editor/Elements/Button/EditorButton.js +28 -16
- package/dist/Editor/Elements/Carousel/CarouselButton.js +2 -1
- package/dist/Editor/Elements/Color Picker/ColorButtons.js +60 -15
- package/dist/Editor/Elements/Color Picker/ColorPicker.css +25 -1
- package/dist/Editor/Elements/Color Picker/ColorPicker.js +4 -4
- package/dist/Editor/Elements/Color Picker/Styles.js +3 -1
- package/dist/Editor/Elements/Emoji/EmojiPicker.js +2 -4
- package/dist/Editor/Elements/Form/Workflow/FormWorkflow.js +12 -3
- package/dist/Editor/Elements/Form/Workflow/UserInputs.js +2 -1
- package/dist/Editor/Elements/Grid/Grid.js +27 -3
- package/dist/Editor/Elements/Grid/GridItem.js +3 -1
- package/dist/Editor/Elements/Link/Link.js +6 -1
- package/dist/Editor/Elements/Link/LinkButton.js +4 -2
- package/dist/Editor/Elements/Link/LinkPopup.js +73 -14
- package/dist/Editor/Elements/Link/LinkPopupStyles.js +28 -0
- package/dist/Editor/Elements/List/CheckList.js +1 -2
- package/dist/Editor/Elements/NewLine/NewLineButton.js +2 -1
- package/dist/Editor/Elements/PageSettings/PageSettingsButton.js +3 -3
- package/dist/Editor/Elements/Redo/RedoButton.js +14 -0
- package/dist/Editor/Elements/Signature/Signature.css +13 -6
- package/dist/Editor/Elements/Signature/SignatureOptions/UploadSignature.js +2 -1
- package/dist/Editor/Elements/Signature/SignaturePopup.js +185 -30
- package/dist/Editor/Elements/SimpleText/index.js +11 -1
- package/dist/Editor/Elements/SimpleText/style.js +1 -1
- package/dist/Editor/Elements/Table/Styles.js +23 -1
- package/dist/Editor/Elements/Table/Table.js +2 -1
- package/dist/Editor/Elements/Table/TableCell.js +69 -7
- package/dist/Editor/Elements/TableContextMenu/TableContextMenu.js +1 -0
- package/dist/Editor/Elements/Undo/UndoButton.js +14 -0
- package/dist/Editor/MiniEditor.js +3 -1
- package/dist/Editor/Styles/EditorStyles.js +1 -1
- package/dist/Editor/Toolbar/Basic/index.js +4 -2
- package/dist/Editor/Toolbar/FormatTools/Dropdown.js +27 -3
- package/dist/Editor/Toolbar/FormatTools/MarkButton.js +2 -2
- package/dist/Editor/Toolbar/FormatTools/TextSize.js +31 -20
- package/dist/Editor/Toolbar/Mini/MiniToolbar.js +36 -5
- package/dist/Editor/Toolbar/Mini/Options/Options.js +10 -0
- package/dist/Editor/Toolbar/Mini/Styles.js +7 -0
- package/dist/Editor/Toolbar/PopupTool/ButtonTemplatesCard.js +12 -13
- package/dist/Editor/Toolbar/PopupTool/FullViewCard.js +12 -13
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/CustomSelectTool.js +3 -0
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/SelectFontSize.js +4 -11
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/SelectSuperSubscript.js +59 -0
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/SelectTypography.js +213 -86
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/index.js +4 -2
- package/dist/Editor/Toolbar/PopupTool/PopupToolStyle.js +123 -44
- package/dist/Editor/Toolbar/PopupTool/TextFormat.js +106 -44
- package/dist/Editor/Toolbar/PopupTool/ThemeTextFormat.js +438 -0
- package/dist/Editor/Toolbar/PopupTool/index.js +7 -6
- package/dist/Editor/Toolbar/toolbarGroups.js +53 -11
- package/dist/Editor/assets/svg/AIIcons.js +153 -1
- package/dist/Editor/assets/svg/AddTemplateIcon.js +13 -10
- package/dist/Editor/assets/svg/RedoIcon.js +27 -0
- package/dist/Editor/assets/svg/SettingsIcon.js +28 -0
- package/dist/Editor/assets/svg/TextIcon.js +8 -5
- package/dist/Editor/assets/svg/ThemeIcons.js +291 -0
- package/dist/Editor/assets/svg/UndoIcon.js +27 -0
- package/dist/Editor/common/ColorPickerButton.js +26 -18
- package/dist/Editor/common/CustomColorPicker/index.js +106 -0
- package/dist/Editor/common/CustomColorPicker/style.js +53 -0
- package/dist/Editor/common/CustomDialog/index.js +94 -0
- package/dist/Editor/common/CustomDialog/style.js +67 -0
- package/dist/Editor/common/CustomSelect.js +33 -0
- package/dist/Editor/common/DnD/DragHandleButton.js +56 -47
- package/dist/Editor/common/EditorIcons.js +7 -7
- package/dist/Editor/common/Icon.js +64 -25
- package/dist/Editor/common/ImageList.js +16 -3
- package/dist/Editor/common/ImageSelector/ImageSelector.js +30 -9
- package/dist/Editor/common/ImageSelector/Styles.js +2 -1
- package/dist/Editor/common/LinkSettings/NavComponents.js +5 -2
- package/dist/Editor/common/LinkSettings/index.js +4 -2
- package/dist/Editor/common/LinkSettings/navOptions.js +7 -2
- package/dist/Editor/common/LinkSettings/style.js +11 -8
- package/dist/Editor/common/MentionsPopup/Styles.js +1 -1
- package/dist/Editor/common/Section/index.js +57 -7
- package/dist/Editor/common/Section/styles.js +11 -0
- package/dist/Editor/common/Shorthands/elements.js +63 -9
- package/dist/Editor/common/StyleBuilder/accordionTitleBtnStyle.js +1 -2
- package/dist/Editor/common/StyleBuilder/buttonStyle.js +4 -2
- package/dist/Editor/common/StyleBuilder/fieldTypes/bannerSpacing.js +13 -3
- package/dist/Editor/common/StyleBuilder/fieldTypes/borderRadius.js +15 -7
- package/dist/Editor/common/StyleBuilder/fieldTypes/buttonLink.js +1 -1
- package/dist/Editor/common/StyleBuilder/fieldTypes/color.js +29 -7
- package/dist/Editor/common/StyleBuilder/fieldTypes/fontSize.js +13 -4
- package/dist/Editor/common/StyleBuilder/fieldTypes/textOptions.js +14 -4
- package/dist/Editor/common/StyleBuilder/index.js +1 -1
- package/dist/Editor/common/iconListV2.js +843 -0
- package/dist/Editor/common/iconslist.js +0 -31
- package/dist/Editor/commonStyle.js +6 -0
- package/dist/Editor/helper/index.js +0 -22
- package/dist/Editor/helper/theme.js +189 -3
- package/dist/Editor/hooks/useEditorTheme.js +139 -0
- package/dist/Editor/hooks/useMouseMove.js +4 -1
- package/dist/Editor/hooks/useWindowMessage.js +10 -7
- package/dist/Editor/plugins/withEmbeds.js +1 -1
- package/dist/Editor/plugins/withHTML.js +1 -1
- package/dist/Editor/plugins/withTable.js +1 -1
- package/dist/Editor/theme/ThemeList.js +50 -173
- package/dist/Editor/theme/index.js +144 -0
- package/dist/Editor/themeSettings/ActiveTheme.js +72 -0
- package/dist/Editor/themeSettings/buttons/index.js +290 -0
- package/dist/Editor/themeSettings/buttons/style.js +21 -0
- package/dist/Editor/themeSettings/colorTheme/index.js +290 -0
- package/dist/Editor/themeSettings/colorTheme/style.js +77 -0
- package/dist/Editor/themeSettings/fonts/PreviewElement.js +123 -0
- package/dist/Editor/themeSettings/fonts/index.js +213 -0
- package/dist/Editor/themeSettings/fonts/style.js +44 -0
- package/dist/Editor/themeSettings/icons.js +60 -0
- package/dist/Editor/themeSettings/index.js +320 -0
- package/dist/Editor/themeSettings/style.js +152 -0
- package/dist/Editor/themeSettingsAI/icons.js +96 -0
- package/dist/Editor/themeSettingsAI/index.js +356 -0
- package/dist/Editor/themeSettingsAI/saveTheme.js +190 -0
- package/dist/Editor/themeSettingsAI/style.js +247 -0
- package/dist/Editor/utils/SlateUtilityFunctions.js +169 -27
- package/dist/Editor/utils/button.js +1 -17
- package/dist/Editor/utils/events.js +54 -2
- package/dist/Editor/utils/font.js +40 -37
- package/dist/Editor/utils/helper.js +31 -2
- package/dist/Editor/utils/table.js +51 -43
- package/package.json +4 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Button, IconButton,
|
|
1
|
+
import { Box, Button, IconButton, Popper, Typography } from "@mui/material";
|
|
2
2
|
import React, { useRef, useState } from "react";
|
|
3
3
|
import { FaChevronRight } from "react-icons/fa";
|
|
4
4
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -49,9 +49,17 @@ function DisplayOption({
|
|
|
49
49
|
} = option;
|
|
50
50
|
const [open, setOpen] = useState(false);
|
|
51
51
|
const optionRef = useRef();
|
|
52
|
+
const openOptions = e => {
|
|
53
|
+
if (option.options?.length && !open) {
|
|
54
|
+
setOpen(e.currentTarget);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
52
58
|
return /*#__PURE__*/_jsxs(Box, {
|
|
53
59
|
sx: classes.optionWrapper,
|
|
54
60
|
ref: optionRef,
|
|
61
|
+
onMouseEnter: openOptions,
|
|
62
|
+
onMouseLeave: () => setOpen(null),
|
|
55
63
|
children: [/*#__PURE__*/_jsxs(Button, {
|
|
56
64
|
sx: classes.optionBtn,
|
|
57
65
|
onClick: e => {
|
|
@@ -59,7 +67,7 @@ function DisplayOption({
|
|
|
59
67
|
|
|
60
68
|
// is having child options
|
|
61
69
|
if (option.options?.length) {
|
|
62
|
-
|
|
70
|
+
openOptions(e);
|
|
63
71
|
return;
|
|
64
72
|
}
|
|
65
73
|
setOpen(null);
|
|
@@ -77,25 +85,24 @@ function DisplayOption({
|
|
|
77
85
|
size: 12
|
|
78
86
|
})
|
|
79
87
|
})]
|
|
80
|
-
}), /*#__PURE__*/_jsx(
|
|
88
|
+
}), /*#__PURE__*/_jsx(Popper, {
|
|
81
89
|
open: open && option.options,
|
|
82
90
|
anchorEl: open,
|
|
83
91
|
sx: {
|
|
84
92
|
zIndex: 9001,
|
|
85
93
|
background: "transparent"
|
|
86
94
|
},
|
|
87
|
-
anchorOrigin: {
|
|
88
|
-
vertical: "top",
|
|
89
|
-
horizontal: "right"
|
|
90
|
-
},
|
|
91
95
|
onClose: () => {
|
|
92
96
|
setOpen(null);
|
|
93
97
|
},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
placement: "right-start",
|
|
99
|
+
children: /*#__PURE__*/_jsx(Box, {
|
|
100
|
+
children: /*#__PURE__*/_jsx(CustomSelect, {
|
|
101
|
+
options: option.options,
|
|
102
|
+
onSend: onSend,
|
|
103
|
+
classes: classes,
|
|
104
|
+
show: open
|
|
105
|
+
})
|
|
99
106
|
})
|
|
100
107
|
})]
|
|
101
108
|
});
|
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useEditorContext } from "../../hooks/useMouseMove";
|
|
3
3
|
import Styles from "./Styles";
|
|
4
|
-
import {
|
|
4
|
+
import { Fade, Paper, Popper } from "@mui/material";
|
|
5
5
|
import AIInput from "./AIInput";
|
|
6
6
|
import { ReactEditor, useSlate } from "slate-react";
|
|
7
|
-
import {
|
|
8
|
-
import useWindowResize from "../../hooks/useWindowResize";
|
|
7
|
+
import { Node, Transforms } from "slate";
|
|
9
8
|
import { MODES } from "./helper";
|
|
10
9
|
import { getSelectedText } from "../../utils/helper";
|
|
10
|
+
import { VoiceToText } from "./VoiceToText";
|
|
11
|
+
import deserialize from "../../helper/deserialize";
|
|
11
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
12
13
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
const scrollToAIInput =
|
|
14
|
+
const scrollToAIInput = editor => {
|
|
14
15
|
try {
|
|
15
16
|
setTimeout(() => {
|
|
16
17
|
const slateWrapper = document.getElementById("slate-wrapper-scroll-container");
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
let selectionRect;
|
|
19
|
+
if (getSelectedText(editor)) {
|
|
20
|
+
selectionRect = window.getSelection().getRangeAt(0).getBoundingClientRect();
|
|
21
|
+
} else {
|
|
22
|
+
selectionRect = ReactEditor.toDOMRange(editor, getNextLine(editor).at).getBoundingClientRect();
|
|
23
|
+
}
|
|
24
|
+
const selectionScrollBottom = selectionRect.bottom;
|
|
25
|
+
|
|
26
|
+
// if the cursor or selection top position is greater than 80
|
|
27
|
+
if (selectionScrollBottom > 80) {
|
|
28
|
+
// scroll to top of the slateWrapper
|
|
22
29
|
slateWrapper.scrollTo({
|
|
23
|
-
top: slateWrapper.scrollTop +
|
|
30
|
+
top: slateWrapper.scrollTop + selectionScrollBottom - 80,
|
|
24
31
|
behavior: "smooth"
|
|
25
32
|
});
|
|
26
33
|
}
|
|
@@ -29,26 +36,19 @@ const scrollToAIInput = () => {
|
|
|
29
36
|
console.log(err);
|
|
30
37
|
}
|
|
31
38
|
};
|
|
39
|
+
const insertText = (editor, text, options) => {
|
|
40
|
+
const parsed = new DOMParser().parseFromString(text, "text/html");
|
|
41
|
+
const fragment = deserialize(parsed.body);
|
|
42
|
+
Transforms.insertFragment(editor, fragment, options);
|
|
43
|
+
};
|
|
32
44
|
const insertAtNextLine = (editor, text) => {
|
|
33
45
|
const nextLine = getNextLine(editor);
|
|
34
|
-
|
|
46
|
+
insertText(editor, text, {
|
|
35
47
|
at: nextLine.at
|
|
36
48
|
});
|
|
37
|
-
Transforms.
|
|
38
|
-
type: "paragraph",
|
|
39
|
-
children: [{
|
|
40
|
-
text
|
|
41
|
-
}]
|
|
42
|
-
}, {
|
|
49
|
+
Transforms.splitNodes(editor, {
|
|
43
50
|
at: nextLine.at
|
|
44
51
|
});
|
|
45
|
-
const currentPath = Path.parent(nextLine.at.focus.path);
|
|
46
|
-
const nextPath = Path.next(currentPath);
|
|
47
|
-
ReactEditor.focus(editor);
|
|
48
|
-
Transforms.select(editor, {
|
|
49
|
-
anchor: Editor.start(editor, nextPath),
|
|
50
|
-
focus: Editor.end(editor, nextPath)
|
|
51
|
-
});
|
|
52
52
|
};
|
|
53
53
|
const getNextLine = editor => {
|
|
54
54
|
const {
|
|
@@ -101,8 +101,14 @@ const updateAnchorEl = (setAnchorEl, editor) => {
|
|
|
101
101
|
// selected text as caret
|
|
102
102
|
caret = selection.getRangeAt(0);
|
|
103
103
|
} else {
|
|
104
|
-
|
|
104
|
+
const domElement = ReactEditor.toDOMRange(editor, getNextLine(editor).at);
|
|
105
|
+
const {
|
|
106
|
+
textContent,
|
|
107
|
+
parentElement
|
|
108
|
+
} = domElement?.commonAncestorContainer || {};
|
|
109
|
+
caret = textContent ? domElement : parentElement; // in mobile, if textContent in not available, it is pointing some <br> tag (getBoundingClientRect not working correctly for <br>), to avoid that, we are pointing the parent element as caret
|
|
105
110
|
}
|
|
111
|
+
|
|
106
112
|
const getBoundingClientRect = () => {
|
|
107
113
|
const editorContainer = document.querySelector("#slate-wrapper-scroll-container")?.getBoundingClientRect();
|
|
108
114
|
const editorEle = document.querySelector(".ed-section-inner")?.getBoundingClientRect();
|
|
@@ -144,10 +150,8 @@ function PopoverAIInput({
|
|
|
144
150
|
const [generatedText, setGeneratedText] = useState("");
|
|
145
151
|
const [inputValue, setInputValue] = useState("");
|
|
146
152
|
const [selectedOption, setSelectedOption] = useState();
|
|
147
|
-
const targetRef = useRef();
|
|
148
153
|
const classes = Styles();
|
|
149
154
|
const editor = useSlate();
|
|
150
|
-
const [size] = useWindowResize();
|
|
151
155
|
const onClickOutside = () => {
|
|
152
156
|
setAnchorEl(null);
|
|
153
157
|
setOpenAI("");
|
|
@@ -163,8 +167,8 @@ function PopoverAIInput({
|
|
|
163
167
|
updateAnchorEl(setAnchorEl, editor);
|
|
164
168
|
}, [openAI, editor.selection]);
|
|
165
169
|
useEffect(() => {
|
|
166
|
-
if (openAI
|
|
167
|
-
scrollToAIInput();
|
|
170
|
+
if (openAI) {
|
|
171
|
+
scrollToAIInput(editor);
|
|
168
172
|
}
|
|
169
173
|
}, [openAI]);
|
|
170
174
|
const onSend = async (type, option) => {
|
|
@@ -172,12 +176,35 @@ function PopoverAIInput({
|
|
|
172
176
|
onClickOutside();
|
|
173
177
|
return;
|
|
174
178
|
}
|
|
179
|
+
if (type === "done") {
|
|
180
|
+
// Get the current selection point
|
|
181
|
+
const {
|
|
182
|
+
anchor
|
|
183
|
+
} = editor.selection;
|
|
184
|
+
const {
|
|
185
|
+
path
|
|
186
|
+
} = anchor;
|
|
187
|
+
const {
|
|
188
|
+
text: selectText
|
|
189
|
+
} = Node.get(editor, path);
|
|
190
|
+
if (selectText?.length) {
|
|
191
|
+
insertAtNextLine(editor, generatedText);
|
|
192
|
+
} else {
|
|
193
|
+
insertText(editor, generatedText);
|
|
194
|
+
}
|
|
195
|
+
onClickOutside();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
175
198
|
if (type === "replace_selection") {
|
|
176
199
|
// replace generated text
|
|
177
|
-
|
|
200
|
+
insertText(editor, generatedText);
|
|
178
201
|
onClickOutside();
|
|
179
202
|
return;
|
|
180
203
|
}
|
|
204
|
+
if (type === "speech_to_text") {
|
|
205
|
+
setGeneratedText(option.text);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
181
208
|
if (type === "try_again") {
|
|
182
209
|
// resetting the previous option and try again
|
|
183
210
|
option = selectedOption;
|
|
@@ -188,7 +215,7 @@ function PopoverAIInput({
|
|
|
188
215
|
setLoading(true);
|
|
189
216
|
const payload = {
|
|
190
217
|
mode: option.mode || 0,
|
|
191
|
-
query: inputValue
|
|
218
|
+
query: option?.inputValue || inputValue
|
|
192
219
|
};
|
|
193
220
|
if (option.mode === MODES.translate || option.mode === MODES.rephraseTone) {
|
|
194
221
|
payload.textOptionInput = type;
|
|
@@ -214,47 +241,7 @@ function PopoverAIInput({
|
|
|
214
241
|
}
|
|
215
242
|
return;
|
|
216
243
|
}
|
|
217
|
-
|
|
218
|
-
// Get the current selection point
|
|
219
|
-
const {
|
|
220
|
-
anchor
|
|
221
|
-
} = editor.selection;
|
|
222
|
-
const {
|
|
223
|
-
path
|
|
224
|
-
} = anchor;
|
|
225
|
-
const {
|
|
226
|
-
text: selectText
|
|
227
|
-
} = Node.get(editor, path);
|
|
228
|
-
const insertInNewLine = option.isSendBtn && selectText?.length || type === "continue_writing";
|
|
229
|
-
if (insertInNewLine) {
|
|
230
|
-
if (getSelectedText(editor)) {
|
|
231
|
-
const currentPath = Path.parent(editor.selection.focus.path);
|
|
232
|
-
const nextPath = Path.next(currentPath);
|
|
233
|
-
Transforms.insertNodes(editor, {
|
|
234
|
-
type: "paragraph",
|
|
235
|
-
children: [{
|
|
236
|
-
text
|
|
237
|
-
}]
|
|
238
|
-
}, {
|
|
239
|
-
at: nextPath,
|
|
240
|
-
select: true
|
|
241
|
-
});
|
|
242
|
-
} else {
|
|
243
|
-
insertAtNextLine(editor, text);
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
Transforms.insertText(editor, text);
|
|
248
|
-
}
|
|
249
|
-
const range = {
|
|
250
|
-
...editor.selection,
|
|
251
|
-
anchor: {
|
|
252
|
-
...anchor,
|
|
253
|
-
offset: openAI === "fromToolBar" ? anchor.offset : 0
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
ReactEditor.focus(editor);
|
|
257
|
-
Transforms.select(editor, range);
|
|
244
|
+
insertText(editor, text);
|
|
258
245
|
|
|
259
246
|
// scrollToAIInput();
|
|
260
247
|
};
|
|
@@ -263,21 +250,7 @@ function PopoverAIInput({
|
|
|
263
250
|
setInputValue(e.target.value);
|
|
264
251
|
};
|
|
265
252
|
return /*#__PURE__*/_jsxs("div", {
|
|
266
|
-
children: [
|
|
267
|
-
component: "div",
|
|
268
|
-
sx: classes.mobileAIInputWrapper,
|
|
269
|
-
ref: targetRef,
|
|
270
|
-
children: /*#__PURE__*/_jsx(AIInput, {
|
|
271
|
-
loading: loading,
|
|
272
|
-
onSend: onSend,
|
|
273
|
-
generatedText: generatedText,
|
|
274
|
-
anchorEl: anchorEl,
|
|
275
|
-
openAI: openAI,
|
|
276
|
-
inputValue: inputValue,
|
|
277
|
-
onInputChange: onInputChange,
|
|
278
|
-
onClickOutside: onClickOutside
|
|
279
|
-
})
|
|
280
|
-
}) : /*#__PURE__*/_jsx(Popper, {
|
|
253
|
+
children: [/*#__PURE__*/_jsx(Popper, {
|
|
281
254
|
open: Boolean(openAI),
|
|
282
255
|
anchorEl: anchorEl,
|
|
283
256
|
transition: true,
|
|
@@ -286,7 +259,6 @@ function PopoverAIInput({
|
|
|
286
259
|
...classes.aiPopper,
|
|
287
260
|
width: editorElement?.offsetWidth || 400
|
|
288
261
|
},
|
|
289
|
-
ref: targetRef,
|
|
290
262
|
children: ({
|
|
291
263
|
TransitionProps
|
|
292
264
|
}) => /*#__PURE__*/_jsx(Fade, {
|
|
@@ -296,21 +268,25 @@ function PopoverAIInput({
|
|
|
296
268
|
sx: getSelectedText(editor) ? {
|
|
297
269
|
marginTop: "6px"
|
|
298
270
|
} : {},
|
|
299
|
-
children: /*#__PURE__*/_jsx(
|
|
300
|
-
|
|
271
|
+
children: /*#__PURE__*/_jsx(VoiceToText, {
|
|
272
|
+
otherProps: otherProps,
|
|
301
273
|
onSend: onSend,
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
274
|
+
children: /*#__PURE__*/_jsx(AIInput, {
|
|
275
|
+
loading: loading,
|
|
276
|
+
onSend: onSend,
|
|
277
|
+
generatedText: generatedText,
|
|
278
|
+
anchorEl: anchorEl,
|
|
279
|
+
openAI: openAI,
|
|
280
|
+
inputValue: inputValue,
|
|
281
|
+
onInputChange: onInputChange,
|
|
282
|
+
onClickOutside: onClickOutside
|
|
283
|
+
})
|
|
308
284
|
})
|
|
309
285
|
})
|
|
310
286
|
})
|
|
311
287
|
}), openAI ? /*#__PURE__*/_jsx("div", {
|
|
312
288
|
style: {
|
|
313
|
-
height:
|
|
289
|
+
height: "100vh",
|
|
314
290
|
background: "transparent"
|
|
315
291
|
}
|
|
316
292
|
}) : null]
|
|
@@ -91,7 +91,6 @@ const Styles = theme => ({
|
|
|
91
91
|
},
|
|
92
92
|
generatedText: {
|
|
93
93
|
margin: "8px",
|
|
94
|
-
maxHeight: "100px",
|
|
95
94
|
overflow: "auto",
|
|
96
95
|
fontSize: "inherit"
|
|
97
96
|
},
|
|
@@ -167,7 +166,8 @@ const Styles = theme => ({
|
|
|
167
166
|
sendIconContainer: {
|
|
168
167
|
alignSelf: "flex-end",
|
|
169
168
|
display: "flex",
|
|
170
|
-
alignItems: "center"
|
|
169
|
+
alignItems: "center",
|
|
170
|
+
gap: "6px"
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
export default Styles;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Box } from "@mui/material";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import WaveSurfer from "wavesurfer.js";
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
const SoundWave = props => {
|
|
6
|
+
const {
|
|
7
|
+
audioChunks = null
|
|
8
|
+
} = props; // Initialize to null if not provided
|
|
9
|
+
const waveContent = useRef(null);
|
|
10
|
+
const wavesurfer = useRef(null);
|
|
11
|
+
const [playOnce, setPlayOnce] = useState(false);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (waveContent.current) {
|
|
14
|
+
if (wavesurfer.current) {
|
|
15
|
+
wavesurfer.current.destroy();
|
|
16
|
+
}
|
|
17
|
+
wavesurfer.current = WaveSurfer.create({
|
|
18
|
+
container: waveContent.current,
|
|
19
|
+
waveColor: "#2563EB",
|
|
20
|
+
progressColor: "#2563EB",
|
|
21
|
+
cursorWidth: 0,
|
|
22
|
+
// Disable the cursor
|
|
23
|
+
barWidth: 2,
|
|
24
|
+
barGap: 1,
|
|
25
|
+
barRadius: 2,
|
|
26
|
+
scrollParent: false,
|
|
27
|
+
// Prevent scrolling
|
|
28
|
+
height: waveContent.current.clientHeight // Set height to fit container
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Fit the waveform to the container width
|
|
32
|
+
const resize = () => {
|
|
33
|
+
if (wavesurfer.current) {
|
|
34
|
+
const containerWidth = waveContent.current.clientWidth;
|
|
35
|
+
const duration = wavesurfer.current.getDuration();
|
|
36
|
+
if (duration > 0) {
|
|
37
|
+
const newZoom = containerWidth / duration;
|
|
38
|
+
wavesurfer.current.zoom(newZoom);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
window.addEventListener("resize", resize);
|
|
43
|
+
resize();
|
|
44
|
+
return () => {
|
|
45
|
+
window.removeEventListener("resize", resize);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}, []);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (wavesurfer.current && audioChunks) {
|
|
51
|
+
const audioBlob = new Blob(audioChunks, {
|
|
52
|
+
type: "audio/webm"
|
|
53
|
+
});
|
|
54
|
+
const recordedUrl = URL.createObjectURL(audioBlob);
|
|
55
|
+
wavesurfer.current.load(recordedUrl).then().catch(e => console.error("Error loading audio: ", e));
|
|
56
|
+
if (!playOnce) {
|
|
57
|
+
setPlayOnce(true);
|
|
58
|
+
}
|
|
59
|
+
} else if (wavesurfer.current && !audioChunks) {
|
|
60
|
+
// Clear the waveform when audioChunks is null
|
|
61
|
+
wavesurfer.current.empty();
|
|
62
|
+
}
|
|
63
|
+
}, [audioChunks, playOnce]);
|
|
64
|
+
return /*#__PURE__*/_jsx(Box, {
|
|
65
|
+
ref: waveContent,
|
|
66
|
+
style: {
|
|
67
|
+
width: "100%",
|
|
68
|
+
height: "100%",
|
|
69
|
+
overflow: "hidden"
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
export const AudioWave = SoundWave;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { useState, useEffect, useRef, cloneElement } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import STTStyles from "./style";
|
|
4
|
+
import { Grid, IconButton, Box } from "@mui/material";
|
|
5
|
+
import { AudioWave } from "./AudioWave";
|
|
6
|
+
import { CloseGreyCircle, PauseRecordingIcon, TickBlueCircle } from "../../../assets/svg/AIIcons";
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
function STT(props) {
|
|
10
|
+
const {
|
|
11
|
+
otherProps,
|
|
12
|
+
onSend,
|
|
13
|
+
setIsRecording
|
|
14
|
+
} = props;
|
|
15
|
+
const {
|
|
16
|
+
services
|
|
17
|
+
} = otherProps;
|
|
18
|
+
const classes = STTStyles();
|
|
19
|
+
const [mediaRecorder, setMediaRecorder] = useState(null);
|
|
20
|
+
const [audioChunks, setAudioChunks] = useState([]);
|
|
21
|
+
const [chunkIndex, setChunkIndex] = useState(0);
|
|
22
|
+
const audioChunksRef = useRef([]);
|
|
23
|
+
const [transcription, setTranscription] = useState("");
|
|
24
|
+
const [showPause, setShowPause] = useState(true);
|
|
25
|
+
const setChunk = event => {
|
|
26
|
+
if (event.data.size > 0) {
|
|
27
|
+
setAudioChunks(prev => {
|
|
28
|
+
const updatedChunks = [...prev, event.data];
|
|
29
|
+
audioChunksRef.current = updatedChunks; // Update ref manually
|
|
30
|
+
return updatedChunks;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const sendChunck = async (isLast = false) => {
|
|
35
|
+
if (audioChunksRef.current.length > 0) {
|
|
36
|
+
const audioBlob = new Blob(audioChunksRef.current, {
|
|
37
|
+
type: "audio/webm"
|
|
38
|
+
});
|
|
39
|
+
const formData = new FormData();
|
|
40
|
+
formData.append("audio", audioBlob, `audio_chunk_${chunkIndex}.wav`);
|
|
41
|
+
formData.append("chunkIndex", chunkIndex); // Send chunk index to the backend
|
|
42
|
+
if (isLast) {
|
|
43
|
+
formData.append("isLastChunk", true); // Send chunk index to the backend
|
|
44
|
+
} else {
|
|
45
|
+
formData.append("isLastChunk", false);
|
|
46
|
+
setChunkIndex(prevIndex => prevIndex + 1);
|
|
47
|
+
}
|
|
48
|
+
const result = await services("speechToText", {
|
|
49
|
+
formData
|
|
50
|
+
});
|
|
51
|
+
setTranscription(result?.data || "");
|
|
52
|
+
await sendChunck();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const handleStopRecording = () => {
|
|
56
|
+
sendChunck(true);
|
|
57
|
+
setAudioChunks([]);
|
|
58
|
+
};
|
|
59
|
+
const startRecording = async () => {
|
|
60
|
+
setTranscription("");
|
|
61
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
62
|
+
audio: true
|
|
63
|
+
});
|
|
64
|
+
const recorder = new MediaRecorder(stream);
|
|
65
|
+
setMediaRecorder(recorder);
|
|
66
|
+
recorder.ondataavailable = setChunk;
|
|
67
|
+
recorder.onstop = handleStopRecording;
|
|
68
|
+
recorder.start(100);
|
|
69
|
+
setIsRecording(true);
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
sendChunck();
|
|
72
|
+
}, 2000);
|
|
73
|
+
};
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
startRecording();
|
|
76
|
+
}, []);
|
|
77
|
+
const stopRecording = () => {
|
|
78
|
+
if (mediaRecorder) {
|
|
79
|
+
mediaRecorder.stop();
|
|
80
|
+
|
|
81
|
+
// Stop all tracks to release the microphone
|
|
82
|
+
if (mediaRecorder.stream) {
|
|
83
|
+
mediaRecorder.stream.getTracks().forEach(track => track.stop());
|
|
84
|
+
}
|
|
85
|
+
setChunkIndex(0);
|
|
86
|
+
setShowPause(false);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const sendToInfiniti = async () => {
|
|
90
|
+
setIsRecording(false);
|
|
91
|
+
await onSend("", {
|
|
92
|
+
inputValue: transcription
|
|
93
|
+
});
|
|
94
|
+
stopRecording();
|
|
95
|
+
setShowPause(true);
|
|
96
|
+
};
|
|
97
|
+
const closeRecording = () => {
|
|
98
|
+
setAudioChunks([]);
|
|
99
|
+
stopRecording();
|
|
100
|
+
setIsRecording(false);
|
|
101
|
+
setShowPause(true);
|
|
102
|
+
setTranscription("");
|
|
103
|
+
};
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
audioChunksRef.current = audioChunks;
|
|
106
|
+
}, [audioChunks]);
|
|
107
|
+
const transcriptionText = typeof transcription === "string" && transcription?.replace(/\s+/g, "")?.length; // Remove all whitespace characters like \n \t
|
|
108
|
+
|
|
109
|
+
return /*#__PURE__*/_jsxs(Grid, {
|
|
110
|
+
xs: 12,
|
|
111
|
+
sx: classes.AudioVizualizerContainer,
|
|
112
|
+
children: [transcriptionText ? /*#__PURE__*/_jsx(Grid, {
|
|
113
|
+
xs: 12,
|
|
114
|
+
sx: classes.TranscriptionContainer,
|
|
115
|
+
children: /*#__PURE__*/_jsx("pre", {
|
|
116
|
+
style: {
|
|
117
|
+
whiteSpace: "pre-wrap",
|
|
118
|
+
wordWrap: "break-word",
|
|
119
|
+
fontFamily: "inherit",
|
|
120
|
+
margin: 0
|
|
121
|
+
},
|
|
122
|
+
children: transcription
|
|
123
|
+
})
|
|
124
|
+
}) : null, /*#__PURE__*/_jsxs(Grid, {
|
|
125
|
+
xs: 12,
|
|
126
|
+
sx: classes.AudioVizualizerContent,
|
|
127
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
128
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
129
|
+
onClick: closeRecording,
|
|
130
|
+
children: /*#__PURE__*/_jsx(CloseGreyCircle, {})
|
|
131
|
+
})
|
|
132
|
+
}), /*#__PURE__*/_jsx(Box, {
|
|
133
|
+
sx: classes.AudioVisualiser,
|
|
134
|
+
children: /*#__PURE__*/_jsx(AudioWave, {
|
|
135
|
+
audioChunks: audioChunks
|
|
136
|
+
})
|
|
137
|
+
}), /*#__PURE__*/_jsx(Box, {
|
|
138
|
+
children: showPause ? /*#__PURE__*/_jsx(IconButton, {
|
|
139
|
+
onClick: stopRecording,
|
|
140
|
+
children: /*#__PURE__*/_jsx(PauseRecordingIcon, {})
|
|
141
|
+
}) : /*#__PURE__*/_jsx(IconButton, {
|
|
142
|
+
onClick: sendToInfiniti,
|
|
143
|
+
disabled: !transcriptionText,
|
|
144
|
+
style: !transcriptionText ? {
|
|
145
|
+
opacity: 0.5
|
|
146
|
+
} : {},
|
|
147
|
+
children: /*#__PURE__*/_jsx(TickBlueCircle, {})
|
|
148
|
+
})
|
|
149
|
+
})]
|
|
150
|
+
})]
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const withHOC = STT => {
|
|
154
|
+
const Container = props => {
|
|
155
|
+
const classes = STTStyles();
|
|
156
|
+
const {
|
|
157
|
+
children
|
|
158
|
+
} = props;
|
|
159
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
160
|
+
return /*#__PURE__*/_jsx(Grid, {
|
|
161
|
+
xs: 12,
|
|
162
|
+
children: /*#__PURE__*/_jsx(Grid, {
|
|
163
|
+
className: classes.SttContainer,
|
|
164
|
+
children: !isRecording ? /*#__PURE__*/_jsx(Grid, {
|
|
165
|
+
xs: 12,
|
|
166
|
+
children: /*#__PURE__*/cloneElement(children, {
|
|
167
|
+
startRecording: () => setIsRecording(true)
|
|
168
|
+
})
|
|
169
|
+
}) : /*#__PURE__*/_jsx(STT, {
|
|
170
|
+
...props,
|
|
171
|
+
setIsRecording: setIsRecording
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
return Container;
|
|
177
|
+
};
|
|
178
|
+
STT.defaultProps = {
|
|
179
|
+
classes: {}
|
|
180
|
+
};
|
|
181
|
+
STT.propTypes = {
|
|
182
|
+
classes: PropTypes.object
|
|
183
|
+
};
|
|
184
|
+
export const VoiceToText = withHOC(STT);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const styles = () => ({
|
|
2
|
+
SttContainer: {
|
|
3
|
+
display: "flex",
|
|
4
|
+
width: "100%",
|
|
5
|
+
padding: "12px",
|
|
6
|
+
zIndex: 1
|
|
7
|
+
},
|
|
8
|
+
STTInput: {
|
|
9
|
+
width: "100%",
|
|
10
|
+
borderRadius: "8px",
|
|
11
|
+
"&& .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline": {
|
|
12
|
+
borderColor: "#2563EB"
|
|
13
|
+
},
|
|
14
|
+
"&& .MuiOutlinedInput-root": {
|
|
15
|
+
background: "rgba(252, 250, 255, 1)"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
AudioVizualizerContainer: {
|
|
19
|
+
width: "100%",
|
|
20
|
+
display: "flex",
|
|
21
|
+
background: "rgba(252, 250, 255, 1)",
|
|
22
|
+
borderRadius: "8px",
|
|
23
|
+
border: "1px solid #2563EB",
|
|
24
|
+
alignItems: "center",
|
|
25
|
+
flexWrap: "wrap"
|
|
26
|
+
},
|
|
27
|
+
AudioVizualizerContent: {
|
|
28
|
+
display: "flex",
|
|
29
|
+
height: "50px",
|
|
30
|
+
width: "100%"
|
|
31
|
+
},
|
|
32
|
+
AudioVisualiser: {
|
|
33
|
+
flexGrow: "1",
|
|
34
|
+
height: "100%"
|
|
35
|
+
},
|
|
36
|
+
TranscriptionContainer: {
|
|
37
|
+
padding: "12px"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
export default styles;
|
|
@@ -47,10 +47,12 @@ export const newContentOptions = [{
|
|
|
47
47
|
groupLabel: "",
|
|
48
48
|
options: [
|
|
49
49
|
// improveWriting,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
{
|
|
51
|
+
label: "Done",
|
|
52
|
+
value: "done",
|
|
53
|
+
Icon: CheckIcon,
|
|
52
54
|
replace: true
|
|
53
|
-
}
|
|
55
|
+
}, ...commonOptions, {
|
|
54
56
|
label: "Close",
|
|
55
57
|
value: "close",
|
|
56
58
|
Icon: CloseIcon,
|