@flozy/editor 3.9.8 → 4.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.
Files changed (130) hide show
  1. package/dist/Editor/ChatEditor.js +55 -45
  2. package/dist/Editor/CommonEditor.js +180 -111
  3. package/dist/Editor/Editor.css +9 -3
  4. package/dist/Editor/Elements/AI/AIInput.js +18 -24
  5. package/dist/Editor/Elements/AI/CustomSelect.js +19 -12
  6. package/dist/Editor/Elements/AI/PopoverAIInput.js +66 -89
  7. package/dist/Editor/Elements/AI/Styles.js +2 -1
  8. package/dist/Editor/Elements/AI/VoiceToText/AudioWave.js +73 -0
  9. package/dist/Editor/Elements/AI/VoiceToText/index.js +184 -0
  10. package/dist/Editor/Elements/AI/VoiceToText/style.js +40 -0
  11. package/dist/Editor/Elements/AI/helper.js +5 -3
  12. package/dist/Editor/Elements/Accordion/Accordion.js +74 -7
  13. package/dist/Editor/Elements/Accordion/AccordionBtnPopup.js +3 -2
  14. package/dist/Editor/Elements/Accordion/AccordionSummary.js +4 -60
  15. package/dist/Editor/Elements/AppHeader/AppHeader.js +26 -4
  16. package/dist/Editor/Elements/Button/EditorButton.js +28 -16
  17. package/dist/Editor/Elements/Color Picker/ColorButtons.js +60 -17
  18. package/dist/Editor/Elements/Color Picker/ColorPicker.css +25 -1
  19. package/dist/Editor/Elements/Color Picker/ColorPicker.js +4 -4
  20. package/dist/Editor/Elements/Color Picker/Styles.js +2 -1
  21. package/dist/Editor/Elements/Embed/Image.js +29 -21
  22. package/dist/Editor/Elements/Embed/Video.js +15 -11
  23. package/dist/Editor/Elements/Emoji/EmojiPicker.js +4 -2
  24. package/dist/Editor/Elements/Form/Form.js +1 -1
  25. package/dist/Editor/Elements/Form/Workflow/FormWorkflow.js +12 -3
  26. package/dist/Editor/Elements/Form/Workflow/UserInputs.js +2 -1
  27. package/dist/Editor/Elements/Grid/Grid.js +33 -16
  28. package/dist/Editor/Elements/Grid/GridItem.js +3 -1
  29. package/dist/Editor/Elements/Link/Link.js +6 -1
  30. package/dist/Editor/Elements/Link/LinkButton.js +4 -2
  31. package/dist/Editor/Elements/Link/LinkPopup.js +10 -3
  32. package/dist/Editor/Elements/Link/LinkPopupStyles.js +28 -0
  33. package/dist/Editor/Elements/PageSettings/PageSettingsButton.js +8 -4
  34. package/dist/Editor/Elements/Redo/RedoButton.js +14 -0
  35. package/dist/Editor/Elements/Signature/SignaturePopup.js +20 -5
  36. package/dist/Editor/Elements/Table/Styles.js +23 -1
  37. package/dist/Editor/Elements/Table/Table.js +3 -2
  38. package/dist/Editor/Elements/Table/TableCell.js +70 -8
  39. package/dist/Editor/Elements/TableContextMenu/TableContextMenu.js +1 -0
  40. package/dist/Editor/Elements/Undo/UndoButton.js +14 -0
  41. package/dist/Editor/MiniEditor.js +3 -1
  42. package/dist/Editor/Styles/EditorStyles.js +1 -1
  43. package/dist/Editor/Toolbar/Basic/index.js +4 -2
  44. package/dist/Editor/Toolbar/FormatTools/Dropdown.js +26 -2
  45. package/dist/Editor/Toolbar/FormatTools/MarkButton.js +2 -2
  46. package/dist/Editor/Toolbar/FormatTools/TextSize.js +29 -18
  47. package/dist/Editor/Toolbar/Mini/MiniToolbar.js +29 -3
  48. package/dist/Editor/Toolbar/Mini/Options/Options.js +10 -0
  49. package/dist/Editor/Toolbar/Mini/Styles.js +7 -0
  50. package/dist/Editor/Toolbar/PopupTool/ButtonTemplatesCard.js +12 -13
  51. package/dist/Editor/Toolbar/PopupTool/FullViewCard.js +12 -13
  52. package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/SelectFontSize.js +4 -11
  53. package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/SelectTypography.js +213 -86
  54. package/dist/Editor/Toolbar/PopupTool/MiniTextFormat/index.js +2 -1
  55. package/dist/Editor/Toolbar/PopupTool/PopupToolStyle.js +40 -33
  56. package/dist/Editor/Toolbar/PopupTool/TextFormat.js +52 -7
  57. package/dist/Editor/Toolbar/PopupTool/ThemeTextFormat.js +438 -0
  58. package/dist/Editor/Toolbar/PopupTool/index.js +7 -6
  59. package/dist/Editor/Toolbar/toolbarGroups.js +48 -6
  60. package/dist/Editor/assets/svg/AIIcons.js +153 -1
  61. package/dist/Editor/assets/svg/AddTemplateIcon.js +13 -10
  62. package/dist/Editor/assets/svg/RedoIcon.js +27 -0
  63. package/dist/Editor/assets/svg/SettingsIcon.js +28 -0
  64. package/dist/Editor/assets/svg/TextIcon.js +8 -5
  65. package/dist/Editor/assets/svg/ThemeIcons.js +291 -0
  66. package/dist/Editor/assets/svg/UndoIcon.js +27 -0
  67. package/dist/Editor/common/ColorPickerButton.js +25 -9
  68. package/dist/Editor/common/CustomColorPicker/index.js +106 -0
  69. package/dist/Editor/common/CustomColorPicker/style.js +53 -0
  70. package/dist/Editor/common/CustomDialog/index.js +94 -0
  71. package/dist/Editor/common/CustomDialog/style.js +67 -0
  72. package/dist/Editor/common/CustomSelect.js +33 -0
  73. package/dist/Editor/common/DnD/DragHandleButton.js +56 -47
  74. package/dist/Editor/common/EditorCmds.js +35 -0
  75. package/dist/Editor/common/Icon.js +43 -3
  76. package/dist/Editor/common/LinkSettings/NavComponents.js +5 -2
  77. package/dist/Editor/common/LinkSettings/index.js +4 -2
  78. package/dist/Editor/common/LinkSettings/navOptions.js +7 -2
  79. package/dist/Editor/common/LinkSettings/style.js +11 -8
  80. package/dist/Editor/common/MentionsPopup/MentionsListCard.js +6 -1
  81. package/dist/Editor/common/MentionsPopup/Styles.js +5 -2
  82. package/dist/Editor/common/Section/index.js +57 -7
  83. package/dist/Editor/common/Section/styles.js +11 -0
  84. package/dist/Editor/common/Shorthands/elements.js +54 -0
  85. package/dist/Editor/common/StyleBuilder/accordionTitleBtnStyle.js +7 -7
  86. package/dist/Editor/common/StyleBuilder/accordionTitleStyle.js +16 -16
  87. package/dist/Editor/common/StyleBuilder/buttonStyle.js +4 -2
  88. package/dist/Editor/common/StyleBuilder/fieldTypes/bannerSpacing.js +13 -3
  89. package/dist/Editor/common/StyleBuilder/fieldTypes/borderRadius.js +15 -7
  90. package/dist/Editor/common/StyleBuilder/fieldTypes/buttonLink.js +1 -1
  91. package/dist/Editor/common/StyleBuilder/fieldTypes/color.js +31 -7
  92. package/dist/Editor/common/StyleBuilder/fieldTypes/fontSize.js +13 -4
  93. package/dist/Editor/common/StyleBuilder/fieldTypes/selectBox.js +14 -3
  94. package/dist/Editor/common/StyleBuilder/fieldTypes/textOptions.js +14 -4
  95. package/dist/Editor/common/StyleBuilder/index.js +1 -1
  96. package/dist/Editor/common/iconslist.js +0 -31
  97. package/dist/Editor/helper/deserialize/index.js +14 -9
  98. package/dist/Editor/helper/theme.js +190 -4
  99. package/dist/Editor/hooks/useEditorTheme.js +139 -0
  100. package/dist/Editor/hooks/useMouseMove.js +4 -2
  101. package/dist/Editor/hooks/useWindowMessage.js +10 -7
  102. package/dist/Editor/plugins/withEmbeds.js +1 -1
  103. package/dist/Editor/plugins/withHTML.js +47 -5
  104. package/dist/Editor/plugins/withLayout.js +15 -10
  105. package/dist/Editor/plugins/withTable.js +2 -2
  106. package/dist/Editor/theme/ThemeList.js +50 -173
  107. package/dist/Editor/theme/index.js +144 -0
  108. package/dist/Editor/themeSettings/ActiveTheme.js +72 -0
  109. package/dist/Editor/themeSettings/buttons/index.js +290 -0
  110. package/dist/Editor/themeSettings/buttons/style.js +21 -0
  111. package/dist/Editor/themeSettings/colorTheme/index.js +290 -0
  112. package/dist/Editor/themeSettings/colorTheme/style.js +77 -0
  113. package/dist/Editor/themeSettings/fonts/PreviewElement.js +123 -0
  114. package/dist/Editor/themeSettings/fonts/index.js +213 -0
  115. package/dist/Editor/themeSettings/fonts/style.js +44 -0
  116. package/dist/Editor/themeSettings/icons.js +60 -0
  117. package/dist/Editor/themeSettings/index.js +320 -0
  118. package/dist/Editor/themeSettings/style.js +152 -0
  119. package/dist/Editor/themeSettingsAI/icons.js +96 -0
  120. package/dist/Editor/themeSettingsAI/index.js +356 -0
  121. package/dist/Editor/themeSettingsAI/saveTheme.js +190 -0
  122. package/dist/Editor/themeSettingsAI/style.js +247 -0
  123. package/dist/Editor/utils/SlateUtilityFunctions.js +161 -25
  124. package/dist/Editor/utils/button.js +1 -17
  125. package/dist/Editor/utils/draftToSlate.js +1 -1
  126. package/dist/Editor/utils/events.js +65 -6
  127. package/dist/Editor/utils/font.js +40 -37
  128. package/dist/Editor/utils/helper.js +74 -14
  129. package/dist/Editor/utils/table.js +51 -43
  130. package/package.json +3 -2
@@ -1,4 +1,4 @@
1
- import { Box, Button, IconButton, Popover, Typography } from "@mui/material";
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
- setOpen(e.currentTarget);
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(Popover, {
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
- children: /*#__PURE__*/_jsx(CustomSelect, {
95
- options: option.options,
96
- onSend: onSend,
97
- classes: classes,
98
- show: open
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,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 { Box, Fade, Paper, Popper } from "@mui/material";
4
+ import { Fade, Paper, Popper } from "@mui/material";
5
5
  import AIInput from "./AIInput";
6
6
  import { ReactEditor, useSlate } from "slate-react";
7
- import { Editor, Node, Path, Transforms } from "slate";
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
- const selectionRect = window.getSelection().getRangeAt(0).getBoundingClientRect();
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
- Transforms.splitNodes(editor, {
45
+ insertText(editor, text, {
35
46
  at: nextLine.at
36
47
  });
37
- Transforms.insertNodes(editor, {
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
- caret = ReactEditor.toDOMRange(editor, getNextLine(editor).at);
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 === "fromToolBar") {
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
- Transforms.insertText(editor, generatedText);
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: [size.device === "xs" && openAI ? /*#__PURE__*/_jsx(Box, {
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(AIInput, {
300
- loading: loading,
272
+ children: /*#__PURE__*/_jsx(VoiceToText, {
273
+ otherProps: otherProps,
301
274
  onSend: onSend,
302
- generatedText: generatedText,
303
- anchorEl: anchorEl,
304
- openAI: openAI,
305
- inputValue: inputValue,
306
- onInputChange: onInputChange,
307
- onClickOutside: onClickOutside
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
  })
@@ -167,7 +167,8 @@ const Styles = theme => ({
167
167
  sendIconContainer: {
168
168
  alignSelf: "flex-end",
169
169
  display: "flex",
170
- alignItems: "center"
170
+ alignItems: "center",
171
+ gap: "6px"
171
172
  }
172
173
  });
173
174
  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
- ...commonOptions.map(o => ({
51
- ...o,
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,