Skip to content

Commit

Permalink
Improve the chat input box (#23)
Browse files Browse the repository at this point in the history
* create an improved chatbox item with widgets

* cleanup examples and example_card for use in chatbox_and_widget

* hide chatbox on the home page

* simplify the landing page with chat_box_and_widget

* enhance ChatBoxAndWidget with search validation and additional text

* allow overriding tools slot in chat_box_and_widget
  • Loading branch information
nwaughachukwuma authored Dec 6, 2024
1 parent 2f5f29a commit 10bba8e
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 58 deletions.
16 changes: 8 additions & 8 deletions api/src/utils/chat_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
]

content_examples: Dict[ContentCategory, str] = {
"podcast": "Create a podcast exploring the intersection of ancient philosophy and artificial intelligence.",
"sermon": "Write a sermon connecting the teachings of Augustine with modern digital ethics.",
"audiodrama": "A reimagining of Homer's Odyssey set in a cyberpunk future.",
"lecture": "A lecture comparing Shakespeare's influence on modern social media communication.",
"commentary": "A commentary on how Classical music influences contemporary electronic genres.",
"voicenote": "A personal reflection on reading Plato's Republic in today's political climate.",
"interview": "An interview with an archaeologist using AI to uncover ancient Roman artifacts.",
"soundbite": "A quick take on how ancient Greek democracy shapes modern blockchain governance.",
"podcast": "Podcast exploring the intersection of ancient philosophy and AI",
"sermon": "Sermon connecting the teachings of Augustine with modern digital ethics",
"audiodrama": "Reimagining of Homer's Odyssey set in a cyberpunk future",
"lecture": "Lecture comparing Shakespeare's influence on modern social media",
"commentary": "Commentary on how Classical music influences contemporary electronic genres",
"voicenote": "Personal reflection on reading Plato's Republic in today's political climate",
"interview": "Interview with an archaeologist using AI to uncover ancient Roman artifacts",
"soundbite": "Quick take on how ancient Greek democracy shapes modern blockchain governance",
}

category_qualifiers: Dict[ContentCategory, str] = {
Expand Down
4 changes: 2 additions & 2 deletions app/src/lib/components/AutoDetectedCategory.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

<ChatListItem
type="assistant"
content="I auto-detected you want {categoryWithArticle}. Press NEXT to continue if correct."
content="I auto-detected you want {categoryWithArticle}. Press NEXT to continue."
/>

<Button
Expand All @@ -40,4 +40,4 @@
<ArrowRight class="w-4 ml-1 inline" />
</Button>

<ChatListItem type="assistant" content="Else, select your audiocast category" />
<ChatListItem type="assistant" content="Or select your audiocast category" />
93 changes: 93 additions & 0 deletions app/src/lib/components/ChatBoxAndWidget.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<script context="module">
const examples = [
{ icon: '🎨', text: 'Create image' },
{ icon: '💡', text: 'Get advice' },
{ icon: '🧠', text: 'Brainstorm' },
{ icon: '🎲', text: 'Surprise me' },
{ icon: '✍️', text: 'Help me write' },
{ text: 'More' }
];
</script>

<script lang="ts">
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';
import { PaperclipIcon, ArrowUpIcon } from 'lucide-svelte';
import { createEventDispatcher } from 'svelte';
export let searchTerm = '';
const dispatch = createEventDispatcher<{
submitSearch: { value: string };
attach: void;
}>();
function handleKeyPress(ev: KeyboardEvent) {
if (ev.key === 'Enter' && !ev.shiftKey && searchTerm) {
ev.preventDefault();
dispatchSearch();
}
}
function dispatchSearch() {
if (!searchTerm.trim()) return;
dispatch('submitSearch', { value: searchTerm });
}
</script>

<div class="flex flex-col items-center max-lg:pt-16 md:justify-center h-full">
<div class="w-full max-w-3xl space-y-8">
<div class="text-center space-y-2">
<h1 class="md:text-4xl text-3xl font-semibold text-white">What can I help with?</h1>
<h3 class="text-base text-gray-400">Listen to anything, anytime</h3>
</div>

<Card class="bg-zinc-800/50 border-0 overflow-hidden">
<div class="flex items-center p-2">
<div class="flex-1">
<textarea
placeholder="Message Audiora"
class="w-full outline-none bg-transparent border-0 focus:ring-0 text-white placeholder-zinc-400 resize-none py-3 px-4"
rows={1}
tabindex={0}
bind:value={searchTerm}
on:keypress={handleKeyPress}
/>
</div>
</div>
<div class="flex flex-row-reverse items-center justify-between p-2 bg-zinc-800/30">
<slot name="tools">
<div class="flex items-center gap-2 px-2">
<Button variant="ghost" size="icon" class="text-zinc-400 hover:text-white" on:click>
<PaperclipIcon class="h-5 w-5" />
</Button>

<Button
variant="ghost"
size="icon"
class="text-zinc-400 hover:text-white"
disabled={!searchTerm}
on:click={dispatchSearch}
>
<ArrowUpIcon class="h-5 w-5" />
</Button>
</div>
</slot>
</div>
</Card>

<slot name="examples">
<div class="flex flex-wrap gap-2 justify-center">
{#each examples as item, index (index)}
<Button
variant="outline"
class="bg-zinc-800/50 border-zinc-700 text-zinc-300 hover:bg-zinc-700/50 hover:text-white"
>
<span class="mr-2">{item.icon}</span>
{item.text}
</Button>
{/each}
</div>
</slot>
</div>
</div>
26 changes: 14 additions & 12 deletions app/src/lib/components/ChatContainer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,18 @@
</div>
</div>

{#if !hasFinalResponse && !$sessionCompleted$ && !$fetchingSource$ && !$audioSource$}
<div class="shrink-0 w-full sm:max-w-xl lg:max-w-3xl max-w-full max-sm:px-4 px-1 py-4">
<ChatBoxContainer
bind:searchTerm
loading={navLoading}
showIcon
disabled={$sessionCompleted$ || disableTextInput}
on:keypress
on:click
/>
</div>
{/if}
<slot name="chatbox">
{#if !hasFinalResponse && !$sessionCompleted$ && !$fetchingSource$ && !$audioSource$}
<div class="shrink-0 w-full sm:max-w-xl lg:max-w-3xl max-w-full max-sm:px-4 px-1 py-4">
<ChatBoxContainer
bind:searchTerm
loading={navLoading}
showIcon
disabled={$sessionCompleted$ || disableTextInput}
on:keypress
on:click
/>
</div>
{/if}
</slot>
</div>
2 changes: 1 addition & 1 deletion app/src/lib/components/ChatListActionItems.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
})
.then((res) => {
$audioSource$ = res;
toast.success('Audiocast source generated successfully');
toast.success('AI-generated source material generated successfully');
})
.catch((error) => toast.error(error.message))
.finally(() => ($fetchingSource$ = false));
Expand Down
2 changes: 1 addition & 1 deletion app/src/lib/components/ExampleCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<button
on:click={handleClick}
class="border no-underline hover:no-underline rounded-md group border-gray-600 p-4 bg-gray-900 hover:bg-gray-800"
class="border rounded-md min-h-[72px] md:h-28 group p-3 bg-zinc-800/50 border-zinc-700 text-zinc-300 hover:bg-zinc-700/50 transition-all hover:text-zinc-100"
>
<span class="text-sm">
{content}
Expand Down
28 changes: 12 additions & 16 deletions app/src/lib/components/RenderExamples.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,27 @@
import type { ContentCategory } from '@/utils/types';
export const contentExamples: Record<ContentCategory, string> = {
podcast:
'Create a podcast exploring the intersection of ancient philosophy and artificial intelligence.',
sermon: 'Write a sermon connecting the teachings of Augustine with modern digital ethics.',
audiodrama: "A reimagining of Homer's Odyssey set in a cyberpunk future.",
lecture: "A lecture comparing Shakespeare's influence on modern social media communication.",
commentary: 'A commentary on how Classical music influences contemporary electronic genres.',
voicenote: "A personal reflection on reading Plato's Republic in today's political climate.",
interview: 'An interview with an archaeologist using AI to uncover ancient Roman artifacts.',
soundbite: 'A quick take on how ancient Greek democracy shapes modern blockchain governance.'
podcast: 'Podcast exploring the intersection of ancient philosophy and AI',
sermon: 'Sermon connecting the teachings of Augustine with modern digital ethics',
audiodrama: "Reimagining of Homer's Odyssey set in a cyberpunk future",
lecture: "Lecture comparing Shakespeare's influence on modern social media",
commentary: 'Commentary on how Classical music influences contemporary electronic genres',
voicenote: "Personal reflection on reading Plato's Republic in today's political climate.",
interview: 'Interview with an archaeologist using AI to uncover ancient Roman artifacts',
soundbite: 'Quick take on how ancient Greek democracy shapes modern blockchain governance'
};
</script>

<script lang="ts">
import ExampleCard from './ExampleCard.svelte';
export let sessionId: string;
$: contentExamplesDict = Object.entries(contentExamples) as Array<
[category: ContentCategory, content: string]
>;
$: contentExamplesDict = (
Object.entries(contentExamples) as Array<[category: ContentCategory, content: string]>
).slice(0, 6);
</script>

<div class="w-full h-full block mt-16">
<div class="w-full mx-auto md:w-4/5 mb-6 text-center">
<h3 class="text-2xl sm:text-3xl font-semibold">Start with one</h3>
</div>
<div class="w-full h-full block">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 pb-4">
{#each contentExamplesDict as [category, content]}
{@const href = `/chat/${sessionId}?category=${category}&chat`}
Expand Down
40 changes: 22 additions & 18 deletions app/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script lang="ts">
import RenderExamples from '@/components/RenderExamples.svelte';
import ChatContainer from '@/components/ChatContainer.svelte';
import { getSessionContext } from '@/stores/sessionContext.svelte.js';
import { uuid } from '@/utils/uuid';
import { goto } from '$app/navigation';
import type { ContentCategory } from '@/utils/types';
import RenderCategorySelection from '@/components/RenderCategorySelection.svelte';
import ChatBoxAndWidget from '@/components/ChatBoxAndWidget.svelte';
import { cn } from '@/utils/ui.utils';
const { sessionId$, addChatItem, startSession } = getSessionContext();
Expand All @@ -23,7 +24,7 @@
async function continueChat(category: ContentCategory) {
startSession(category);
const content = `${selectContent}\nCategory: ${category} `;
addChatItem({ id: uuid(), content, role: 'user', loading: false, createdAt: Date.now() });
Expand All @@ -36,20 +37,23 @@
<title>Audiora</title>
</svelte:head>

<ChatContainer
disableTextInput={triggerSelectCategory}
bind:searchTerm
on:click={handleSearch}
on:keypress={handleSearch}
<div
class={cn('overflow-auto w-full h-full flex items-center justify-center mx-auto px-4', {
'flex-col justify-start': triggerSelectCategory && selectContent
})}
>
<svelte:fragment slot="content">
{#if triggerSelectCategory && selectContent}
<RenderCategorySelection
content={selectContent}
on:selectCategory={({ detail }) => continueChat(detail.value)}
/>
{:else}
<RenderExamples {sessionId} />
{/if}
</svelte:fragment>
</ChatContainer>
<div class="sm:max-w-xl lg:max-w-3xl max-w-full w-full max-h-full">
<div class="scrollbar-y-1 w-full h-full block">
{#if triggerSelectCategory && selectContent}
<RenderCategorySelection
content={selectContent}
on:selectCategory={({ detail }) => continueChat(detail.value)}
/>
{:else}
<ChatBoxAndWidget bind:searchTerm on:submitSearch={handleSearch}>
<RenderExamples slot="examples" {sessionId} />
</ChatBoxAndWidget>
{/if}
</div>
</div>
</div>

0 comments on commit 10bba8e

Please sign in to comment.