@flozy/editor 3.9.7 → 3.9.9
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/CommonEditor.js +177 -109
- package/dist/Editor/Editor.css +7 -0
- package/dist/Editor/Elements/AI/AIInput.js +18 -24
- package/dist/Editor/Elements/AI/CustomSelect.js +19 -12
- package/dist/Editor/Elements/AI/PopoverAIInput.js +66 -89
- package/dist/Editor/Elements/AI/Styles.js +2 -1
- 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/AppHeader/AppHeader.js +26 -4
- package/dist/Editor/Elements/Button/EditorButton.js +28 -16
- package/dist/Editor/Elements/Color Picker/ColorButtons.js +60 -17
- 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 +2 -1
- 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 +10 -3
- package/dist/Editor/Elements/Link/LinkPopupStyles.js +28 -0
- package/dist/Editor/Elements/PageSettings/PageSettingsButton.js +3 -3
- package/dist/Editor/Elements/Redo/RedoButton.js +14 -0
- package/dist/Editor/Elements/Signature/SignaturePopup.js +20 -5
- 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 +26 -2
- package/dist/Editor/Toolbar/FormatTools/MarkButton.js +2 -2
- package/dist/Editor/Toolbar/FormatTools/TextSize.js +5 -11
- package/dist/Editor/Toolbar/Mini/MiniToolbar.js +4 -2
- 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/SelectFontSize.js +4 -11
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/SelectTypography.js +213 -86
- package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/index.js +2 -1
- package/dist/Editor/Toolbar/PopupTool/PopupToolStyle.js +40 -33
- package/dist/Editor/Toolbar/PopupTool/TextFormat.js +52 -7
- package/dist/Editor/Toolbar/PopupTool/ThemeTextFormat.js +438 -0
- package/dist/Editor/Toolbar/PopupTool/index.js +2 -2
- package/dist/Editor/Toolbar/toolbarGroups.js +48 -6
- 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 +25 -9
- 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/Icon.js +43 -3
- 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/Section/index.js +57 -7
- package/dist/Editor/common/Section/styles.js +11 -0
- package/dist/Editor/common/Shorthands/elements.js +54 -0
- 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 +31 -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/iconslist.js +0 -31
- package/dist/Editor/helper/theme.js +190 -4
- 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 +161 -25
- 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 +3 -2
|
@@ -1,20 +1,26 @@
|
|
|
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
|
+
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
|
+
}
|
|
18
24
|
const halfOfWrapper = slateWrapper.clientHeight / 2;
|
|
19
25
|
const selectionScollTop = selectionRect.y + selectionRect.height;
|
|
20
26
|
if (selectionScollTop > halfOfWrapper) {
|
|
@@ -29,26 +35,19 @@ const scrollToAIInput = () => {
|
|
|
29
35
|
console.log(err);
|
|
30
36
|
}
|
|
31
37
|
};
|
|
38
|
+
const insertText = (editor, text, options) => {
|
|
39
|
+
const parsed = new DOMParser().parseFromString(text, "text/html");
|
|
40
|
+
const fragment = deserialize(parsed.body);
|
|
41
|
+
Transforms.insertFragment(editor, fragment, options);
|
|
42
|
+
};
|
|
32
43
|
const insertAtNextLine = (editor, text) => {
|
|
33
44
|
const nextLine = getNextLine(editor);
|
|
34
|
-
|
|
45
|
+
insertText(editor, text, {
|
|
35
46
|
at: nextLine.at
|
|
36
47
|
});
|
|
37
|
-
Transforms.
|
|
38
|
-
type: "paragraph",
|
|
39
|
-
children: [{
|
|
40
|
-
text
|
|
41
|
-
}]
|
|
42
|
-
}, {
|
|
48
|
+
Transforms.splitNodes(editor, {
|
|
43
49
|
at: nextLine.at
|
|
44
50
|
});
|
|
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
51
|
};
|
|
53
52
|
const getNextLine = editor => {
|
|
54
53
|
const {
|
|
@@ -101,8 +100,14 @@ const updateAnchorEl = (setAnchorEl, editor) => {
|
|
|
101
100
|
// selected text as caret
|
|
102
101
|
caret = selection.getRangeAt(0);
|
|
103
102
|
} else {
|
|
104
|
-
|
|
103
|
+
const domElement = ReactEditor.toDOMRange(editor, getNextLine(editor).at);
|
|
104
|
+
const {
|
|
105
|
+
textContent,
|
|
106
|
+
parentElement
|
|
107
|
+
} = domElement?.commonAncestorContainer || {};
|
|
108
|
+
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
109
|
}
|
|
110
|
+
|
|
106
111
|
const getBoundingClientRect = () => {
|
|
107
112
|
const editorContainer = document.querySelector("#slate-wrapper-scroll-container")?.getBoundingClientRect();
|
|
108
113
|
const editorEle = document.querySelector(".ed-section-inner")?.getBoundingClientRect();
|
|
@@ -147,7 +152,6 @@ function PopoverAIInput({
|
|
|
147
152
|
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,
|
|
@@ -296,15 +269,19 @@ function PopoverAIInput({
|
|
|
296
269
|
sx: getSelectedText(editor) ? {
|
|
297
270
|
marginTop: "6px"
|
|
298
271
|
} : {},
|
|
299
|
-
children: /*#__PURE__*/_jsx(
|
|
300
|
-
|
|
272
|
+
children: /*#__PURE__*/_jsx(VoiceToText, {
|
|
273
|
+
otherProps: otherProps,
|
|
301
274
|
onSend: onSend,
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
275
|
+
children: /*#__PURE__*/_jsx(AIInput, {
|
|
276
|
+
loading: loading,
|
|
277
|
+
onSend: onSend,
|
|
278
|
+
generatedText: generatedText,
|
|
279
|
+
anchorEl: anchorEl,
|
|
280
|
+
openAI: openAI,
|
|
281
|
+
inputValue: inputValue,
|
|
282
|
+
onInputChange: onInputChange,
|
|
283
|
+
onClickOutside: onClickOutside
|
|
284
|
+
})
|
|
308
285
|
})
|
|
309
286
|
})
|
|
310
287
|
})
|
|
@@ -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,
|
|
@@ -62,6 +62,11 @@ function AppHeader(props) {
|
|
|
62
62
|
const handleDrawerToggle = () => {
|
|
63
63
|
setMobileOpen(prevState => !prevState);
|
|
64
64
|
};
|
|
65
|
+
const closeDrawer = () => {
|
|
66
|
+
if (mobileOpen) {
|
|
67
|
+
handleDrawerToggle();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
65
70
|
const onSettings = e => {
|
|
66
71
|
if (!readOnly) {
|
|
67
72
|
e.stopPropagation();
|
|
@@ -148,10 +153,27 @@ function AppHeader(props) {
|
|
|
148
153
|
}), /*#__PURE__*/_jsx(Divider, {}), /*#__PURE__*/_jsx(List, {
|
|
149
154
|
children: menus.map((item, i) => {
|
|
150
155
|
const buttonProps = handleLinkType(item.url, item.linkType, true, item.target === "_blank");
|
|
156
|
+
const onTouchEnd = e => {
|
|
157
|
+
if (buttonProps?.onTouchEnd) {
|
|
158
|
+
buttonProps?.onTouchEnd(e);
|
|
159
|
+
closeDrawer();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const onClick = e => {
|
|
163
|
+
if (buttonProps?.onClick) {
|
|
164
|
+
buttonProps?.onClick(e);
|
|
165
|
+
closeDrawer();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const props = {
|
|
169
|
+
...buttonProps,
|
|
170
|
+
onTouchEnd,
|
|
171
|
+
onClick
|
|
172
|
+
};
|
|
151
173
|
return /*#__PURE__*/_jsx(ListItem, {
|
|
152
174
|
disablePadding: true,
|
|
153
175
|
children: /*#__PURE__*/_jsx(ListItemButton, {
|
|
154
|
-
...
|
|
176
|
+
...props,
|
|
155
177
|
sx: {
|
|
156
178
|
textAlign: "center"
|
|
157
179
|
},
|
|
@@ -229,7 +251,7 @@ function AppHeader(props) {
|
|
|
229
251
|
style: {
|
|
230
252
|
display: "inline-flex",
|
|
231
253
|
alignItems: "center",
|
|
232
|
-
color: textColor,
|
|
254
|
+
color: textColor || "#000000",
|
|
233
255
|
fontSize: logoFontSize,
|
|
234
256
|
fontFamily: titleFontFamily,
|
|
235
257
|
justifyContent: isLogoRight ? "end" : "start"
|
|
@@ -277,7 +299,7 @@ function AppHeader(props) {
|
|
|
277
299
|
fontFamily: fontFamily,
|
|
278
300
|
textTransform: "none",
|
|
279
301
|
fontSize: fontSize || "16px",
|
|
280
|
-
color: textColor || "#
|
|
302
|
+
color: textColor || "#000",
|
|
281
303
|
background: bgColor || "none",
|
|
282
304
|
"& .m-settings": {
|
|
283
305
|
display: "none",
|
|
@@ -292,7 +314,7 @@ function AppHeader(props) {
|
|
|
292
314
|
background: "#FFF"
|
|
293
315
|
},
|
|
294
316
|
"&:hover": {
|
|
295
|
-
color: textColorHover || textColor || "#
|
|
317
|
+
color: textColorHover || textColor || "#000",
|
|
296
318
|
background: bgColorHover || bgColor || "none",
|
|
297
319
|
"& .m-settings": {
|
|
298
320
|
display: "block"
|