Spaces:
Running
Running
add speech recognition
Browse files- module.d.ts +1 -0
- package-lock.json +25 -0
- package.json +2 -0
- src/components/App.tsx +0 -2
- src/components/ask-ai/ask-ai.tsx +3 -0
- src/components/speech-prompt/speech-prompt.tsx +46 -0
module.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
declare module "react-speech-recognition";
|
package-lock.json
CHANGED
@@ -22,6 +22,7 @@
|
|
22 |
"react-dom": "^19.0.0",
|
23 |
"react-icons": "^5.5.0",
|
24 |
"react-markdown": "^10.1.0",
|
|
|
25 |
"react-toastify": "^11.0.5",
|
26 |
"react-use": "^17.6.0",
|
27 |
"tailwindcss": "^4.0.15"
|
@@ -31,6 +32,7 @@
|
|
31 |
"@types/express": "^5.0.1",
|
32 |
"@types/react": "^19.0.10",
|
33 |
"@types/react-dom": "^19.0.4",
|
|
|
34 |
"@vitejs/plugin-react": "^4.3.4",
|
35 |
"eslint": "^9.21.0",
|
36 |
"eslint-plugin-react-hooks": "^5.1.0",
|
@@ -1608,6 +1610,12 @@
|
|
1608 |
"@types/ms": "*"
|
1609 |
}
|
1610 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
1611 |
"node_modules/@types/estree": {
|
1612 |
"version": "1.0.6",
|
1613 |
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
@@ -1730,6 +1738,15 @@
|
|
1730 |
"@types/react": "^19.0.0"
|
1731 |
}
|
1732 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1733 |
"node_modules/@types/send": {
|
1734 |
"version": "0.17.4",
|
1735 |
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
@@ -5370,6 +5387,14 @@
|
|
5370 |
"node": ">=0.10.0"
|
5371 |
}
|
5372 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5373 |
"node_modules/react-toastify": {
|
5374 |
"version": "11.0.5",
|
5375 |
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
|
|
22 |
"react-dom": "^19.0.0",
|
23 |
"react-icons": "^5.5.0",
|
24 |
"react-markdown": "^10.1.0",
|
25 |
+
"react-speech-recognition": "^4.0.0",
|
26 |
"react-toastify": "^11.0.5",
|
27 |
"react-use": "^17.6.0",
|
28 |
"tailwindcss": "^4.0.15"
|
|
|
32 |
"@types/express": "^5.0.1",
|
33 |
"@types/react": "^19.0.10",
|
34 |
"@types/react-dom": "^19.0.4",
|
35 |
+
"@types/react-speech-recognition": "^3.9.6",
|
36 |
"@vitejs/plugin-react": "^4.3.4",
|
37 |
"eslint": "^9.21.0",
|
38 |
"eslint-plugin-react-hooks": "^5.1.0",
|
|
|
1610 |
"@types/ms": "*"
|
1611 |
}
|
1612 |
},
|
1613 |
+
"node_modules/@types/dom-speech-recognition": {
|
1614 |
+
"version": "0.0.6",
|
1615 |
+
"resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.6.tgz",
|
1616 |
+
"integrity": "sha512-o7pAVq9UQPJL5RDjO1f/fcpfFHdgiMnR4PoIU2N/ZQrYOS3C5rzdOJMsrpqeBCbii2EE9mERXgqspQqPDdPahw==",
|
1617 |
+
"dev": true
|
1618 |
+
},
|
1619 |
"node_modules/@types/estree": {
|
1620 |
"version": "1.0.6",
|
1621 |
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
|
|
1738 |
"@types/react": "^19.0.0"
|
1739 |
}
|
1740 |
},
|
1741 |
+
"node_modules/@types/react-speech-recognition": {
|
1742 |
+
"version": "3.9.6",
|
1743 |
+
"resolved": "https://registry.npmjs.org/@types/react-speech-recognition/-/react-speech-recognition-3.9.6.tgz",
|
1744 |
+
"integrity": "sha512-cdzwXIZXWyp8zfM2XI7APDW1rZf4Nz73T4SIS2y+cC7zHnZluCdumYKH6HacxgxJH+zemAq2oXbHWXcyW0eT3A==",
|
1745 |
+
"dev": true,
|
1746 |
+
"dependencies": {
|
1747 |
+
"@types/dom-speech-recognition": "*"
|
1748 |
+
}
|
1749 |
+
},
|
1750 |
"node_modules/@types/send": {
|
1751 |
"version": "0.17.4",
|
1752 |
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
|
|
5387 |
"node": ">=0.10.0"
|
5388 |
}
|
5389 |
},
|
5390 |
+
"node_modules/react-speech-recognition": {
|
5391 |
+
"version": "4.0.0",
|
5392 |
+
"resolved": "https://registry.npmjs.org/react-speech-recognition/-/react-speech-recognition-4.0.0.tgz",
|
5393 |
+
"integrity": "sha512-hz1OsRhjAW70rOMVXN84PR+1L2I1j8xS1TXpwpd4vlDaRY9i/LbAaxEklqscgObECTTuyxNeGBdVdcq/pX3bqQ==",
|
5394 |
+
"peerDependencies": {
|
5395 |
+
"react": ">=16.8.0"
|
5396 |
+
}
|
5397 |
+
},
|
5398 |
"node_modules/react-toastify": {
|
5399 |
"version": "11.0.5",
|
5400 |
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
package.json
CHANGED
@@ -25,6 +25,7 @@
|
|
25 |
"react-dom": "^19.0.0",
|
26 |
"react-icons": "^5.5.0",
|
27 |
"react-markdown": "^10.1.0",
|
|
|
28 |
"react-toastify": "^11.0.5",
|
29 |
"react-use": "^17.6.0",
|
30 |
"tailwindcss": "^4.0.15"
|
@@ -34,6 +35,7 @@
|
|
34 |
"@types/express": "^5.0.1",
|
35 |
"@types/react": "^19.0.10",
|
36 |
"@types/react-dom": "^19.0.4",
|
|
|
37 |
"@vitejs/plugin-react": "^4.3.4",
|
38 |
"eslint": "^9.21.0",
|
39 |
"eslint-plugin-react-hooks": "^5.1.0",
|
|
|
25 |
"react-dom": "^19.0.0",
|
26 |
"react-icons": "^5.5.0",
|
27 |
"react-markdown": "^10.1.0",
|
28 |
+
"react-speech-recognition": "^4.0.0",
|
29 |
"react-toastify": "^11.0.5",
|
30 |
"react-use": "^17.6.0",
|
31 |
"tailwindcss": "^4.0.15"
|
|
|
35 |
"@types/express": "^5.0.1",
|
36 |
"@types/react": "^19.0.10",
|
37 |
"@types/react-dom": "^19.0.4",
|
38 |
+
"@types/react-speech-recognition": "^3.9.6",
|
39 |
"@vitejs/plugin-react": "^4.3.4",
|
40 |
"eslint": "^9.21.0",
|
41 |
"eslint-plugin-react-hooks": "^5.1.0",
|
src/components/App.tsx
CHANGED
@@ -23,8 +23,6 @@ function App() {
|
|
23 |
const [htmlStorage, , removeHtmlStorage] = useLocalStorage("html_content");
|
24 |
const remix = useSearchParam("remix");
|
25 |
|
26 |
-
console.log("REMIX => ", remix);
|
27 |
-
|
28 |
const preview = useRef<HTMLDivElement>(null);
|
29 |
const editor = useRef<HTMLDivElement>(null);
|
30 |
const resizer = useRef<HTMLDivElement>(null);
|
|
|
23 |
const [htmlStorage, , removeHtmlStorage] = useLocalStorage("html_content");
|
24 |
const remix = useSearchParam("remix");
|
25 |
|
|
|
|
|
26 |
const preview = useRef<HTMLDivElement>(null);
|
27 |
const editor = useRef<HTMLDivElement>(null);
|
28 |
const resizer = useRef<HTMLDivElement>(null);
|
src/components/ask-ai/ask-ai.tsx
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { useState } from "react";
|
2 |
import { RiSparkling2Fill } from "react-icons/ri";
|
3 |
import { GrSend } from "react-icons/gr";
|
@@ -10,6 +11,7 @@ import Login from "../login/login";
|
|
10 |
import { defaultHTML } from "./../../../utils/consts";
|
11 |
import SuccessSound from "./../../assets/success.mp3";
|
12 |
import Settings from "../settings/settings";
|
|
|
13 |
|
14 |
function AskAI({
|
15 |
html,
|
@@ -166,6 +168,7 @@ function AskAI({
|
|
166 |
}}
|
167 |
/>
|
168 |
<div className="flex items-center justify-end gap-2">
|
|
|
169 |
<Settings
|
170 |
provider={provider as string}
|
171 |
onChange={setProvider}
|
|
|
1 |
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2 |
import { useState } from "react";
|
3 |
import { RiSparkling2Fill } from "react-icons/ri";
|
4 |
import { GrSend } from "react-icons/gr";
|
|
|
11 |
import { defaultHTML } from "./../../../utils/consts";
|
12 |
import SuccessSound from "./../../assets/success.mp3";
|
13 |
import Settings from "../settings/settings";
|
14 |
+
import SpeechPrompt from "../speech-prompt/speech-prompt";
|
15 |
|
16 |
function AskAI({
|
17 |
html,
|
|
|
168 |
}}
|
169 |
/>
|
170 |
<div className="flex items-center justify-end gap-2">
|
171 |
+
<SpeechPrompt setPrompt={setPrompt} />
|
172 |
<Settings
|
173 |
provider={provider as string}
|
174 |
onChange={setProvider}
|
src/components/speech-prompt/speech-prompt.tsx
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import classNames from "classnames";
|
2 |
+
import { FaMicrophone } from "react-icons/fa";
|
3 |
+
import SpeechRecognition, {
|
4 |
+
useSpeechRecognition,
|
5 |
+
} from "react-speech-recognition";
|
6 |
+
import { useUpdateEffect } from "react-use";
|
7 |
+
|
8 |
+
function SpeechPrompt({
|
9 |
+
setPrompt,
|
10 |
+
}: {
|
11 |
+
setPrompt: React.Dispatch<React.SetStateAction<string>>;
|
12 |
+
}) {
|
13 |
+
const { transcript, listening, browserSupportsSpeechRecognition } =
|
14 |
+
useSpeechRecognition();
|
15 |
+
|
16 |
+
const startListening = () =>
|
17 |
+
SpeechRecognition.startListening({ continuous: true });
|
18 |
+
|
19 |
+
if (!browserSupportsSpeechRecognition) {
|
20 |
+
return null;
|
21 |
+
}
|
22 |
+
|
23 |
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
24 |
+
useUpdateEffect(() => {
|
25 |
+
if (transcript) setPrompt(transcript);
|
26 |
+
}, [transcript]);
|
27 |
+
|
28 |
+
return (
|
29 |
+
<button
|
30 |
+
className={classNames(
|
31 |
+
"flex cursor-pointer flex-none items-center justify-center rounded-full text-sm font-semibold size-8 text-center bg-gray-800 hover:bg-gray-700 text-white shadow-sm dark:shadow-highlight/20 disabled:bg-gray-300 disabled:text-gray-500 disabled:cursor-not-allowed disabled:hover:bg-gray-300",
|
32 |
+
{
|
33 |
+
"animate-pulse !bg-orange-500": listening,
|
34 |
+
}
|
35 |
+
)}
|
36 |
+
onTouchStart={startListening}
|
37 |
+
onMouseDown={startListening}
|
38 |
+
onTouchEnd={SpeechRecognition.stopListening}
|
39 |
+
onMouseUp={SpeechRecognition.stopListening}
|
40 |
+
>
|
41 |
+
<FaMicrophone className="text-base" />
|
42 |
+
</button>
|
43 |
+
);
|
44 |
+
}
|
45 |
+
|
46 |
+
export default SpeechPrompt;
|