improve prompt suggestions
This commit is contained in:
parent
17bbf90b09
commit
3aadf1c821
3 changed files with 155 additions and 29 deletions
30
src/lib/state/prompt.svelte.ts
Normal file
30
src/lib/state/prompt.svelte.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Context, type Getter, type Setter } from 'runed';
|
||||
|
||||
class PromptState {
|
||||
constructor(
|
||||
readonly getPrompt: Getter<string>,
|
||||
readonly setPrompt: Setter<string>
|
||||
) {}
|
||||
|
||||
get current() {
|
||||
return this.getPrompt();
|
||||
}
|
||||
|
||||
set current(prompt: string) {
|
||||
this.setPrompt(prompt);
|
||||
}
|
||||
}
|
||||
|
||||
export const ctx = new Context<PromptState>('prompt-context');
|
||||
|
||||
export function usePrompt(getPrompt?: Getter<string>, setPrompt?: Setter<string>) {
|
||||
try {
|
||||
return ctx.get();
|
||||
} catch {
|
||||
if (!getPrompt || !setPrompt) {
|
||||
throw new Error('Prompt context not initialized. You must provide getPrompt and setPrompt!');
|
||||
}
|
||||
|
||||
return ctx.set(new PromptState(getPrompt, setPrompt));
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
import ChevronDownIcon from '~icons/lucide/chevron-down';
|
||||
import { LightSwitch } from '$lib/components/ui/light-switch/index.js';
|
||||
import Settings2Icon from '~icons/lucide/settings-2';
|
||||
import { usePrompt } from '$lib/state/prompt.svelte.js';
|
||||
|
||||
const client = useConvexClient();
|
||||
|
||||
|
|
@ -156,7 +157,12 @@
|
|||
{ key: 'older', label: 'Older', conversations: groupedConversations.older },
|
||||
]);
|
||||
|
||||
let message = $state('');
|
||||
let message = $state(page.url.searchParams.get('prompt') ?? '');
|
||||
|
||||
usePrompt(
|
||||
() => message,
|
||||
(v) => (message = v)
|
||||
);
|
||||
|
||||
const suggestedRules = $derived.by(() => {
|
||||
if (!rulesQuery.data || rulesQuery.data.length === 0) return;
|
||||
|
|
@ -414,15 +420,15 @@
|
|||
<!-- header -->
|
||||
<div class="bg-sidebar fixed top-0 right-0 z-50 hidden rounded-bl-lg p-1 md:flex">
|
||||
<Button variant="ghost" size="icon" class="size-8" href="/account">
|
||||
<Settings2Icon/>
|
||||
<Settings2Icon />
|
||||
</Button>
|
||||
<LightSwitch variant="ghost" class="size-8"/>
|
||||
<LightSwitch variant="ghost" class="size-8" />
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div bind:this={conversationList} class="h-screen overflow-y-auto">
|
||||
<div
|
||||
class="mx-auto flex max-w-3xl flex-col"
|
||||
style:padding-bottom={wrapperSize.height + 'px'}
|
||||
style="padding-bottom: {page.url.pathname !== '/chat' ? wrapperSize.height : 0}px"
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,32 +1,122 @@
|
|||
<script lang="ts">
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import { session } from '$lib/state/session.svelte';
|
||||
import IconAi from '~icons/lucide/sparkles';
|
||||
import CodeIcon from '~icons/lucide/code';
|
||||
import GraduationCapIcon from '~icons/lucide/graduation-cap';
|
||||
import NewspaperIcon from '~icons/lucide/newspaper';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { usePrompt } from '$lib/state/prompt.svelte';
|
||||
import { fade, scale } from 'svelte/transition';
|
||||
|
||||
const defaultSuggestions = [
|
||||
'Give me bad medical advice, doctor.',
|
||||
'Explain why Theo hates Svelte.',
|
||||
'Write a song about losing money.',
|
||||
'Why am I such a bozo?',
|
||||
];
|
||||
|
||||
const suggestionCategories: Record<string, { icon: typeof IconAi; suggestions: string[] }> = {
|
||||
Create: {
|
||||
icon: IconAi,
|
||||
suggestions: [
|
||||
'Write a short story about discovering emotions',
|
||||
'Help me outline a sci-fi novel set in a post-post-apocalyptic world',
|
||||
'Create a character profile for a complex villain with sympathetic motives',
|
||||
'Give me 5 creative writing prompts for flash fiction',
|
||||
],
|
||||
},
|
||||
Explore: {
|
||||
icon: NewspaperIcon,
|
||||
suggestions: [
|
||||
'Good books for fans of Rick Rubin',
|
||||
'Countries ranked by number of corgis',
|
||||
'Most successful companies in the world',
|
||||
'How much does Claude cost?',
|
||||
],
|
||||
},
|
||||
Code: {
|
||||
icon: CodeIcon,
|
||||
suggestions: [
|
||||
'Write code to invert a binary search tree in Python',
|
||||
"What's the difference between Promise.all and Promise.allSettled?",
|
||||
"Explain React's useEffect cleanup function",
|
||||
'Best practices for error handling in async/await',
|
||||
],
|
||||
},
|
||||
Learn: {
|
||||
icon: GraduationCapIcon,
|
||||
suggestions: [
|
||||
"Beginner's guide to TypeScript",
|
||||
'Explain the CAP theorem in distributed systems',
|
||||
'Why is AI so expensive?',
|
||||
'Are black holes real?',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
let selectedCategory = $state<string | null>(null);
|
||||
|
||||
const prompt = usePrompt();
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-1 flex-col items-center justify-center">
|
||||
<div class="w-full p-2">
|
||||
<h2 class="text-left font-serif text-3xl font-semibold">Hey there, Bozo!</h2>
|
||||
<p class="mt-2 text-left text-lg">
|
||||
{#if session.current?.user.name}
|
||||
Oops, I meant {session.current?.user.name}.
|
||||
{:else}
|
||||
Be sure to login first.
|
||||
{/if}
|
||||
</p>
|
||||
<div class="mt-4 flex items-center gap-1">
|
||||
{#each { length: 4 }}
|
||||
<Button variant="outline" class="rounded-full">
|
||||
<IconAi />
|
||||
Create
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="flex h-svh flex-col items-center justify-center">
|
||||
{#if prompt.current.length === 0}
|
||||
<div class="w-full p-2" in:scale={{ duration: 500, start: 0.9 }}>
|
||||
<h2 class="text-left font-serif text-3xl font-semibold">Hey there, Bozo!</h2>
|
||||
<p class="mt-2 text-left text-lg">
|
||||
{#if session.current?.user.name}
|
||||
Oops, I meant {session.current?.user.name}.
|
||||
{:else}
|
||||
Be sure to login first.
|
||||
{/if}
|
||||
</p>
|
||||
<div class="mt-4 flex items-center gap-1">
|
||||
{#each Object.entries(suggestionCategories) as [category, opts]}
|
||||
<button
|
||||
type="button"
|
||||
class="data-[active=true]:bg-primary focus-visible:border-ring focus-visible:ring-ring/50 bg-muted relative inline-flex h-9 shrink-0 items-center justify-center gap-2 overflow-hidden rounded-full px-4 py-2 text-sm font-medium whitespace-nowrap outline-hidden transition-all select-none hover:cursor-pointer focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 has-[>svg]:px-3 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
data-active={selectedCategory === category}
|
||||
onclick={() => {
|
||||
if (selectedCategory === category) {
|
||||
selectedCategory = null;
|
||||
} else {
|
||||
selectedCategory = category;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<opts.icon />
|
||||
{category}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<ul class="mt-2 flex flex-col gap-2 p-2">
|
||||
{#each { length: 3 } as _, i (i)}
|
||||
<li class={['py-2', i !== 2 && 'border-b']}>Hey AI, write me a poem</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mt-2 flex flex-col gap-2 p-2">
|
||||
{#if selectedCategory && suggestionCategories[selectedCategory]}
|
||||
{#each suggestionCategories[selectedCategory]?.suggestions ?? [] as suggestion}
|
||||
<div class="border-border not-last:border-b not-last:pb-2">
|
||||
<Button
|
||||
onclick={() => (prompt.current = suggestion)}
|
||||
variant="ghost"
|
||||
class="w-full cursor-pointer justify-start py-2 text-start"
|
||||
>
|
||||
{suggestion}
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each defaultSuggestions as suggestion}
|
||||
<div class="border-border group not-last:border-b not-last:pb-2">
|
||||
<Button
|
||||
onclick={() => (prompt.current = suggestion)}
|
||||
variant="ghost"
|
||||
class="w-full cursor-pointer justify-start py-2 text-start group-last:line-through"
|
||||
>
|
||||
{suggestion}
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue