Skip to content

Commit 7e26d79

Browse files
authored
feat: options stored in memory, responsiveness, input (#31)
* fix: LCP of app Relates to #29 * fix: accessibility * fix: LCP, performance increase from 50 to 70% * fix: Fix mobile responsive, store voice * fix: layout to avoid duplication of MessageAudio * fix: build failed because of types * fix: border to 2rem
1 parent 89e5f51 commit 7e26d79

File tree

15 files changed

+154
-92
lines changed

15 files changed

+154
-92
lines changed

app/components/Controls.tsx

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ import { SendIcon } from "./icons/SendIcon";
88
import { Settings } from "./Settings";
99
import { useMicrophone } from "../context/Microphone";
1010
import { useNowPlaying } from "react-nowplaying";
11+
import { useSubmit } from "../lib/hooks/useSubmit";
12+
13+
// Better to use library, a lot of complexity is involved
14+
// in building the resizable input
15+
import TextareaAutosize from 'react-textarea-autosize';
16+
1117

1218
export const Controls = ({
1319
input,
@@ -21,6 +27,7 @@ export const Controls = ({
2127
messages: Message[];
2228
}) => {
2329
const { startMicrophone, stopMicrophone, microphoneOpen } = useMicrophone();
30+
const { formRef, onKeyDown } = useSubmit()
2431

2532
useEffect(() => {
2633
startMicrophone();
@@ -46,21 +53,23 @@ export const Controls = ({
4653
(e: any) => {
4754
handleSubmit(e);
4855
stopAudio();
56+
e.target.value = '';
57+
handleInputChange(e)
4958
},
5059
// eslint-disable-next-line react-hooks/exhaustive-deps
5160
[stopAudio, handleSubmit]
5261
);
5362

5463
return (
55-
<form onSubmit={submitter}>
64+
<form onSubmit={submitter} ref={formRef}>
5665
<div className="relative">
5766
<div className="absolute w-full -top-[4.5rem] py-4 flex justify-between">
5867
<Settings />
5968
<Download messages={messages} />
6069
</div>
6170
<div className="flex bg-[#101014] rounded-full">
6271
<span
63-
className={`rounded-s-full ps-0.5 py-0.5 ${
72+
className={`rounded-tl-[2rem] rounded-bl-[2rem] ps-0.5 py-0.5 ${
6473
microphoneOpen
6574
? "bg-gradient-to-r bg-gradient to-[#13EF93]/50 from-red-500"
6675
: "bg-gradient-to-r bg-gradient to-[#13EF93]/50 from-[#149AFB]/80"
@@ -70,18 +79,18 @@ export const Controls = ({
7079
<a
7180
href="#"
7281
onClick={(e: any) => microphoneToggle(e)}
73-
className={`w-20 sm:w-24 py-4 px-2 sm:px-8 rounded-s-full font-bold bg-[#101014] text-light-900 text-sm sm:text-base flex items-center justify-center group`}
82+
className={`rounded-tl-[2rem] rounded-bl-[2rem] w-16 md:w-20 sm:w-24 py-2 md:py-4 px-2 h-full sm:px-8 font-bold bg-[#101014] text-light-900 text-sm sm:text-base flex items-center justify-center group`}
7483
>
7584
{microphoneOpen && (
7685
<div className="w-auto items-center justify-center hidden sm:flex absolute shrink-0">
7786
<MicrophoneIcon
7887
micOpen={microphoneOpen}
79-
className="h-6 animate-ping-short"
88+
className="h-5 md:h-6 animate-ping-short"
8089
/>
8190
</div>
8291
)}
8392
<div className="w-auto flex items-center justify-center shrink-0">
84-
<MicrophoneIcon micOpen={microphoneOpen} className="h-6" />
93+
<MicrophoneIcon micOpen={microphoneOpen} className="h-5 md:h-6" />
8594
</div>
8695
{/* <span>
8796
{microphoneOpen ? (
@@ -94,24 +103,30 @@ export const Controls = ({
94103
</Tooltip>
95104
</span>
96105

97-
<span className="flex-grow bg-[#13EF93]/50 py-0.5">
98-
<input
99-
type="text"
100-
className="py-4 sm:px-4 w-full h-full bg-[#101014] text-light-900 border-0 text-sm sm:text-base outline-none focus:ring-0"
101-
placeholder="Type a message to send..."
102-
value={input}
103-
onChange={handleInputChange}
104-
/>
105-
</span>
106+
<div className="flex-grow bg-[#13EF93]/50 py-0.5 inline">
107+
<div className=" bg-[#101014] h-full">
108+
<TextareaAutosize
109+
onKeyDown={onKeyDown}
110+
rows={1}
111+
spellCheck={false}
112+
autoCorrect="off"
113+
className="py-2 md:py-4 -mb-[0.4rem] min-h-10 overflow-hidden sm:px-4 w-full resize-none bg-[#101014] text-light-900 border-0 text-sm sm:text-base outline-none focus:ring-0"
114+
placeholder="Send a message"
115+
value={input}
116+
onChange={handleInputChange}
117+
/>
118+
</div>
106119

107-
<span className="rounded-e-full bg-gradient-to-l to-[#13EF93]/50 from-[#149AFB]/80 pe-0.5 py-0.5">
120+
</div>
121+
122+
<div className="inline h-auto rounded-tr-[2rem] rounded-br-[2rem] bg-gradient-to-l to-[#13EF93]/50 from-[#149AFB]/80 pe-0.5 py-0.5">
108123
<Tooltip showArrow content="Send a message.">
109-
<button className="w-20 sm:w-24 py-4 px-2 sm:px-8 rounded-e-full font-bold bg-[#101014] text-light-900 text-sm sm:text-base flex items-center justify-center">
124+
<button type="submit" className="w-16 md:w-24 h-full py-2 md:py-4 px-2 rounded-tr-[2rem] rounded-br-[2rem] font-bold bg-[#101014] text-light-900 text-sm sm:text-base flex items-center justify-center">
110125
{/* <span>Send text</span> */}
111-
<SendIcon className="h-6 w-6" />
126+
<SendIcon className="w-5 md:w-6" />
112127
</button>
113128
</Tooltip>
114-
</span>
129+
</div>
115130
</div>
116131
</div>
117132
</form>

app/components/Conversation.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,7 @@ export default function Conversation(): JSX.Element {
452452
>
453453
<div className="grid grid-cols-12 overflow-x-auto gap-y-2">
454454
{initialLoad ? (
455-
<InitialLoad
456-
fn={startConversation}
457-
connecting={!connection}
458-
/>
455+
<InitialLoad fn={startConversation} connecting={!connection} />
459456
) : (
460457
<>
461458
{chatMessages.length > 0 &&

app/components/Download.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const DownloadButton = ({ content }: { content: string }) => {
99
return (
1010
<span className="bg-white/10 rounded-full flex">
1111
<a
12-
className={`relative m-px bg-black w-[10.5rem] md:w-10 h-10 rounded-full text-sm p-2.5 group hover:w-[10.5rem] transition-all ease-in-out duration-1000 overflow-hidden whitespace-nowrap`}
12+
className={`relative m-px bg-black md:w-[10.5rem] w-10 h-10 rounded-full text-sm p-2.5 group md:hover:w-[10.5rem] transition-all ease-in-out duration-1000 overflow-hidden whitespace-nowrap`}
1313
download="transcript.txt"
1414
target="_blank"
1515
rel="noreferrer"

app/components/InitialLoad.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import { ExclamationIcon } from "./icons/ExclamationIcon";
21
import { Headphones } from "./Headphones";
3-
import { isBrowser, isIOS } from "react-device-detect";
4-
import Image from "next/image";
2+
import { isBrowser } from "react-device-detect";
53
import { Spinner } from "@nextui-org/react";
64

75
export const InitialLoad = ({ fn, connecting = true }: { fn: () => void, connecting: boolean }) => {
@@ -27,9 +25,9 @@ export const InitialLoad = ({ fn, connecting = true }: { fn: () => void, connect
2725
</ul>
2826
</div>
2927
<span className="mt-4 block font-semibold">
30-
<div className="bg-white text-black rounded px-10 py-3 font-semibold sm:w-fit sm:mx-auto opacity-90">
28+
<div className="bg-white text-black rounded px-6 md:px-8 py-3 font-semibold sm:w-fit sm:mx-auto opacity-90">
3129
{connecting ? (
32-
<div className="w-auto h-full items-center flex justify-center opacity-40 cursor-not-allowed">
30+
<div className="w-full h-full items-center flex justify-center opacity-40 cursor-not-allowed">
3331
<Spinner size={"sm"} className="-mt-1 mr-2" />
3432
Connecting...
3533
</div>

app/components/LeftBubble.tsx

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,31 @@ import { TextContent } from "./TextContext";
88
export const LeftBubble = ({ message }: { message: Message }) => {
99
return (
1010
<>
11-
<div className="col-start-1 col-end-13 sm:col-end-11 md:col-end-9 lg:col-end-8 xl:col-end-7 px-3 pt-3">
12-
<div className="flex items-start gap-2">
13-
<div className="h-5 w-12 text-white shrink-0">
14-
<AgentAvatar message={message} />
15-
</div>
16-
<div className="glass flex p-4 rounded-e-xl rounded-es-xl">
17-
<div className="flex flex-col">
18-
<MessageHeader message={message} />
19-
<div className="text-sm font-normal pt-2 text-white/80 markdown">
20-
<TextContent text={message.content} />
11+
<div className="col-start-1 col-end-13 sm:col-end-11 md:col-end-9 lg:col-end-8 xl:col-end-7 md:px-3 pt-3">
12+
<div className="flex items-start gap-2 flex-col md:flex-row">
13+
<div className="flex items-start gap-2 flex-col md:flex-row max-w-full md:max-w-none">
14+
<div className="min-w-12 text-white shrink-0">
15+
<AgentAvatar message={message} />
16+
</div>
17+
<div className="glass flex p-4 rounded-e-xl rounded-es-xl max-w-full md:max-w-none">
18+
<div className="flex flex-col overflow-hidden pre-overflow-y-auto">
19+
<MessageHeader message={message} />
20+
<div className="text-sm font-normal pt-2 text-white/80 markdown">
21+
<TextContent text={message.content} />
22+
</div>
2123
</div>
2224
</div>
2325
</div>
24-
<div className="h-6 w-6 shrink-0 self-center">
25-
<MessageAudio message={message} />
26+
<div className="md:px-1 pb-3 flex gap-2 self-start md:self-center">
27+
<div className="h-6 w-6 shrink-0">
28+
<MessageAudio message={message} />
29+
</div>
30+
<MessageMeta className="md:hidden" message={message} />
2631
</div>
2732
</div>
2833
</div>
29-
<div className="col-start-1 col-end-13 px-3 pb-3">
30-
<MessageMeta className="ml-14" message={message} />
34+
<div className="hidden col-start-1 col-end-13 md:px-3 pb-3 md:flex gap-2">
35+
<MessageMeta className="md:ml-14" message={message} />
3136
</div>
3237
</>
3338
);

app/components/MessageMeta.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ const MessageMeta = ({
3838
const ttsTotal = foundAudio.networkLatency;
3939

4040
return (
41-
<>
41+
<div className="flex flex-col">
4242
<div
43-
className={`flex gap-x-2.5 text-xs text-[#BBBBBF] ${className} flex-wrap`}
43+
className={`flex gap-x-2.5 pt-1 text-xs text-[#BBBBBF] ${className} flex-wrap`}
4444
>
4545
<span>
4646
<BoltIcon className="w-[1em] h-[1em]" />
@@ -81,7 +81,7 @@ const MessageMeta = ({
8181
TTS total: {(ttsTotal / 1000).toFixed(1)}s
8282
</span>
8383
</div>
84-
</>
84+
</div>
8585
);
8686
}
8787
};

app/components/RightBubble.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,20 @@ export const RightBubble = ({
1212
}) => {
1313
return (
1414
<>
15-
<div className="col-start-6 col-end-13 p-3">
16-
<div className="flex justify-start flex-row-reverse gap-2">
17-
<div className="h-6 w-6 text-white shrink-0 pt-1 mt-1 rounded-full bg-black border border-zinc-300 overflow-hidden">
18-
<UserAvatar />
19-
</div>
20-
<div className="glass relative text-sm py-2 px-4 shadow rounded-s-xl rounded-ee-xl">
21-
<div className="text-sm font-normal text-white/80 markdown min-w-[10em]">
22-
<TextContent text={message?.content ?? text ?? ""} />
15+
<div className="col-start-1 col-end-13 md:p-3">
16+
<div className="flex flex-row justify-end">
17+
<div className="flex justify-end md:justify-start gap-2 flex-col md:flex-row-reverse">
18+
<div className="self-end md:self-start h-6 w-6 text-white shrink-0 pt-1 mt-1 rounded-full bg-black border border-zinc-300 overflow-hidden">
19+
<UserAvatar />
20+
</div>
21+
<div className="glass relative text-sm py-2 px-4 shadow rounded-s-xl rounded-ee-xl">
22+
<div className="text-sm font-normal text-white/80 markdown word-break">
23+
<TextContent text={message?.content ?? text ?? ""} />
24+
</div>
2325
</div>
2426
</div>
2527
</div>
28+
2629
</div>
2730
</>
2831
);

app/components/Settings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export const Settings = () => {
117117
<div className="flex items-center gap-2.5 text-sm">
118118
<span className="bg-gradient-to-r to-[#13EF93]/50 from-[#149AFB]/80 rounded-full flex">
119119
<a
120-
className={`relative m-px bg-black w-[9.25rem] md:w-10 h-10 rounded-full text-sm p-2.5 group hover:w-[9.25rem] transition-all ease-in-out duration-1000 overflow-hidden whitespace-nowrap`}
120+
className={`relative m-px bg-black md:w-[9.25rem] w-10 h-10 rounded-full text-sm p-2.5 group md:hover:w-[9.25rem] transition-all ease-in-out duration-1000 overflow-hidden whitespace-nowrap`}
121121
href="#"
122122
onClick={onOpen}
123123
>

app/context/Deepgram.tsx

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ import {
1717
useState,
1818
} from "react";
1919
import { useToast } from "./Toast";
20+
import { useLocalStorage } from "../lib/hooks/useLocalStorage";
2021

2122
type DeepgramContext = {
22-
ttsOptions: SpeakSchema | undefined;
23-
setTtsOptions: Dispatch<SetStateAction<SpeakSchema | undefined>>;
24-
sttOptions: LiveSchema | undefined;
25-
setSttOptions: Dispatch<SetStateAction<LiveSchema | undefined>>;
23+
ttsOptions: SpeakSchema;
24+
setTtsOptions: (value: SpeakSchema) => void;
25+
sttOptions: LiveSchema;
26+
setSttOptions: (value: LiveSchema) => void;
2627
connection: LiveClient | undefined;
2728
connectionReady: boolean;
2829
};
@@ -33,6 +34,9 @@ interface DeepgramContextInterface {
3334

3435
const DeepgramContext = createContext({} as DeepgramContext);
3536

37+
const DEFAULT_TTS_MODEL = 'aura-asteria-en';
38+
const DEFAULT_STT_MODEL = 'nova-2';
39+
;
3640
/**
3741
* TTS Voice Options
3842
*/
@@ -44,7 +48,7 @@ const voices: {
4448
accent: string;
4549
};
4650
} = {
47-
"aura-asteria-en": {
51+
[DEFAULT_TTS_MODEL]: {
4852
name: "Asteria",
4953
avatar: "/aura-asteria-en.svg",
5054
language: "English",
@@ -132,8 +136,17 @@ const getApiKey = async (): Promise<string> => {
132136

133137
const DeepgramContextProvider = ({ children }: DeepgramContextInterface) => {
134138
const { toast } = useToast();
135-
const [ttsOptions, setTtsOptions] = useState<SpeakSchema>();
136-
const [sttOptions, setSttOptions] = useState<LiveSchema>();
139+
const [ttsOptions, setTtsOptions] = useLocalStorage<SpeakSchema>('ttsModel', {
140+
model: DEFAULT_TTS_MODEL
141+
});
142+
const [sttOptions, setSttOptions] = useLocalStorage<LiveSchema>('sttModel', {
143+
model: DEFAULT_STT_MODEL,
144+
interim_results: true,
145+
smart_format: true,
146+
endpointing: 350,
147+
utterance_end_ms: 1000,
148+
filler_words: true,
149+
});
137150
const [connection, setConnection] = useState<LiveClient>();
138151
const [connecting, setConnecting] = useState<boolean>(false);
139152
const [connectionReady, setConnectionReady] = useState<boolean>(false);
@@ -145,14 +158,7 @@ const DeepgramContextProvider = ({ children }: DeepgramContextInterface) => {
145158
const connection = new LiveClient(
146159
await getApiKey(),
147160
{},
148-
{
149-
model: "nova-2",
150-
interim_results: true,
151-
smart_format: true,
152-
endpointing: 550,
153-
utterance_end_ms: 1500,
154-
filler_words: true,
155-
}
161+
sttOptions
156162
);
157163

158164
setConnection(connection);
@@ -164,26 +170,6 @@ const DeepgramContextProvider = ({ children }: DeepgramContextInterface) => {
164170
useEffect(() => {
165171
// it must be the first open of the page, let's set up the defaults
166172

167-
/**
168-
* Default TTS Voice when the app loads.
169-
*/
170-
if (ttsOptions === undefined) {
171-
setTtsOptions({
172-
model: "aura-asteria-en",
173-
});
174-
}
175-
176-
if (!sttOptions === undefined) {
177-
setSttOptions({
178-
model: "nova-2",
179-
interim_results: true,
180-
smart_format: true,
181-
endpointing: 350,
182-
utterance_end_ms: 1000,
183-
filler_words: true,
184-
});
185-
}
186-
187173
if (connection === undefined) {
188174
connect();
189175
}

app/globals.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ body {
6868
@apply leading-normal break-words;
6969
}
7070

71+
.pre-overflow-y-auto pre {
72+
@apply overflow-y-auto;
73+
}
74+
75+
.word-break {
76+
word-break: break-word;
77+
}
7178
.markdown > * + * {
7279
@apply my-2;
7380
}

0 commit comments

Comments
 (0)