diff --git a/app/components/Controls.tsx b/app/components/Controls.tsx
index d6822e13..3dc5d2a1 100644
--- a/app/components/Controls.tsx
+++ b/app/components/Controls.tsx
@@ -8,6 +8,12 @@ import { SendIcon } from "./icons/SendIcon";
import { Settings } from "./Settings";
import { useMicrophone } from "../context/Microphone";
import { useNowPlaying } from "react-nowplaying";
+import { useSubmit } from "../lib/hooks/useSubmit";
+
+// Better to use library, a lot of complexity is involved
+// in building the resizable input
+import TextareaAutosize from 'react-textarea-autosize';
+
export const Controls = ({
input,
@@ -21,6 +27,7 @@ export const Controls = ({
messages: Message[];
}) => {
const { startMicrophone, stopMicrophone, microphoneOpen } = useMicrophone();
+ const { formRef, onKeyDown } = useSubmit()
useEffect(() => {
startMicrophone();
@@ -46,13 +53,15 @@ export const Controls = ({
(e: any) => {
handleSubmit(e);
stopAudio();
+ e.target.value = '';
+ handleInputChange(e)
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[stopAudio, handleSubmit]
);
return (
-
diff --git a/app/components/Conversation.tsx b/app/components/Conversation.tsx
index 9f715a10..66c90bd5 100644
--- a/app/components/Conversation.tsx
+++ b/app/components/Conversation.tsx
@@ -452,10 +452,7 @@ export default function Conversation(): JSX.Element {
>
-
+
{connecting ? (
-
+
Connecting...
diff --git a/app/components/LeftBubble.tsx b/app/components/LeftBubble.tsx
index 467165b6..746f3caf 100644
--- a/app/components/LeftBubble.tsx
+++ b/app/components/LeftBubble.tsx
@@ -8,26 +8,31 @@ import { TextContent } from "./TextContext";
export const LeftBubble = ({ message }: { message: Message }) => {
return (
<>
-
-
-
-
-
-
-
-
+
+
-
-
+
+
>
);
diff --git a/app/components/MessageMeta.tsx b/app/components/MessageMeta.tsx
index 0e928733..68f6857d 100644
--- a/app/components/MessageMeta.tsx
+++ b/app/components/MessageMeta.tsx
@@ -38,9 +38,9 @@ const MessageMeta = ({
const ttsTotal = foundAudio.networkLatency;
return (
- <>
+
@@ -81,7 +81,7 @@ const MessageMeta = ({
TTS total: {(ttsTotal / 1000).toFixed(1)}s
- >
+
);
}
};
diff --git a/app/components/RightBubble.tsx b/app/components/RightBubble.tsx
index fa537f74..819d5b5f 100644
--- a/app/components/RightBubble.tsx
+++ b/app/components/RightBubble.tsx
@@ -12,17 +12,20 @@ export const RightBubble = ({
}) => {
return (
<>
-
-
-
-
-
-
-
-
+
>
);
diff --git a/app/components/Settings.tsx b/app/components/Settings.tsx
index 918b8767..a2f7e86b 100644
--- a/app/components/Settings.tsx
+++ b/app/components/Settings.tsx
@@ -117,7 +117,7 @@ export const Settings = () => {
diff --git a/app/context/Deepgram.tsx b/app/context/Deepgram.tsx
index 358bc7b1..0e8213a7 100644
--- a/app/context/Deepgram.tsx
+++ b/app/context/Deepgram.tsx
@@ -17,12 +17,13 @@ import {
useState,
} from "react";
import { useToast } from "./Toast";
+import { useLocalStorage } from "../lib/hooks/useLocalStorage";
type DeepgramContext = {
- ttsOptions: SpeakSchema | undefined;
- setTtsOptions: Dispatch>;
- sttOptions: LiveSchema | undefined;
- setSttOptions: Dispatch>;
+ ttsOptions: SpeakSchema;
+ setTtsOptions: (value: SpeakSchema) => void;
+ sttOptions: LiveSchema;
+ setSttOptions: (value: LiveSchema) => void;
connection: LiveClient | undefined;
connectionReady: boolean;
};
@@ -33,6 +34,9 @@ interface DeepgramContextInterface {
const DeepgramContext = createContext({} as DeepgramContext);
+const DEFAULT_TTS_MODEL = 'aura-asteria-en';
+const DEFAULT_STT_MODEL = 'nova-2';
+;
/**
* TTS Voice Options
*/
@@ -44,7 +48,7 @@ const voices: {
accent: string;
};
} = {
- "aura-asteria-en": {
+ [DEFAULT_TTS_MODEL]: {
name: "Asteria",
avatar: "/aura-asteria-en.svg",
language: "English",
@@ -132,8 +136,17 @@ const getApiKey = async (): Promise => {
const DeepgramContextProvider = ({ children }: DeepgramContextInterface) => {
const { toast } = useToast();
- const [ttsOptions, setTtsOptions] = useState();
- const [sttOptions, setSttOptions] = useState();
+ const [ttsOptions, setTtsOptions] = useLocalStorage('ttsModel', {
+ model: DEFAULT_TTS_MODEL
+ });
+ const [sttOptions, setSttOptions] = useLocalStorage('sttModel', {
+ model: DEFAULT_STT_MODEL,
+ interim_results: true,
+ smart_format: true,
+ endpointing: 350,
+ utterance_end_ms: 1000,
+ filler_words: true,
+ });
const [connection, setConnection] = useState();
const [connecting, setConnecting] = useState(false);
const [connectionReady, setConnectionReady] = useState(false);
@@ -145,14 +158,7 @@ const DeepgramContextProvider = ({ children }: DeepgramContextInterface) => {
const connection = new LiveClient(
await getApiKey(),
{},
- {
- model: "nova-2",
- interim_results: true,
- smart_format: true,
- endpointing: 550,
- utterance_end_ms: 1500,
- filler_words: true,
- }
+ sttOptions
);
setConnection(connection);
@@ -164,26 +170,6 @@ const DeepgramContextProvider = ({ children }: DeepgramContextInterface) => {
useEffect(() => {
// it must be the first open of the page, let's set up the defaults
- /**
- * Default TTS Voice when the app loads.
- */
- if (ttsOptions === undefined) {
- setTtsOptions({
- model: "aura-asteria-en",
- });
- }
-
- if (!sttOptions === undefined) {
- setSttOptions({
- model: "nova-2",
- interim_results: true,
- smart_format: true,
- endpointing: 350,
- utterance_end_ms: 1000,
- filler_words: true,
- });
- }
-
if (connection === undefined) {
connect();
}
diff --git a/app/globals.css b/app/globals.css
index f09b225f..8a0c5a08 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -68,6 +68,13 @@ body {
@apply leading-normal break-words;
}
+.pre-overflow-y-auto pre {
+ @apply overflow-y-auto;
+}
+
+.word-break {
+ word-break: break-word;
+}
.markdown > * + * {
@apply my-2;
}
diff --git a/app/lib/hooks/useLocalStorage.ts b/app/lib/hooks/useLocalStorage.ts
new file mode 100644
index 00000000..b8eff6c3
--- /dev/null
+++ b/app/lib/hooks/useLocalStorage.ts
@@ -0,0 +1,24 @@
+import { useEffect, useState } from 'react'
+
+export const useLocalStorage = (
+ key: string,
+ initialValue: T
+): [T, (value: T) => void] => {
+ const [storedValue, setStoredValue] = useState(initialValue)
+
+ useEffect(() => {
+ // Retrieve from localStorage
+ const item = window.localStorage.getItem(key)
+ if (item) {
+ setStoredValue(JSON.parse(item))
+ }
+ }, [key])
+
+ const setValue = (value: T) => {
+ // Save state
+ setStoredValue(value)
+ // Save to localStorage
+ window.localStorage.setItem(key, JSON.stringify(value))
+ }
+ return [storedValue, setValue]
+}
\ No newline at end of file
diff --git a/app/lib/hooks/useSubmit.tsx b/app/lib/hooks/useSubmit.tsx
new file mode 100644
index 00000000..7a22a6ad
--- /dev/null
+++ b/app/lib/hooks/useSubmit.tsx
@@ -0,0 +1,25 @@
+// help, taken from,
+// https://github.com/vercel/ai-chatbot/blob/fa9f0947f0a7983cf5022cbbc1416910349dd5e4/lib/hooks/use-enter-submit.tsx
+import { useRef, type RefObject } from 'react'
+
+export function useSubmit(): {
+ formRef: RefObject
+ onKeyDown: (event: React.KeyboardEvent) => void
+} {
+ const formRef = useRef(null)
+
+ const handleKeyDown = (
+ event: React.KeyboardEvent
+ ): void => {
+ if (
+ !event.shiftKey &&
+ !event.nativeEvent.isComposing &&
+ event.key === 'Enter'
+ ) {
+ formRef.current?.requestSubmit()
+ event.preventDefault()
+ }
+ }
+
+ return { formRef, onKeyDown: handleKeyDown }
+}
\ No newline at end of file
diff --git a/app/page.tsx b/app/page.tsx
index 0af434de..cae5fcd9 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -21,11 +21,11 @@ export default function Home() {