From fd9407d681e0200a0921a1f045f19ac12c1d250a Mon Sep 17 00:00:00 2001 From: Aidan Bleser <117548273+ieedan@users.noreply.github.com> Date: Wed, 18 Jun 2025 13:16:16 -0500 Subject: [PATCH] styling fixes (#24) --- src/lib/backend/convex/conversations.ts | 11 +- src/lib/backend/convex/messages.ts | 3 + src/lib/components/app-sidebar.svelte | 46 +++++- src/lib/components/ui/sidebar/sidebar.svelte | 10 +- .../components/ui/sidebar/sidebar.svelte.ts | 5 + src/routes/api/generate-message/call.ts | 28 ++-- src/routes/chat/+layout.svelte | 131 +++++++++++++----- src/routes/chat/search-modal.svelte | 25 +--- 8 files changed, 179 insertions(+), 80 deletions(-) diff --git a/src/lib/backend/convex/conversations.ts b/src/lib/backend/convex/conversations.ts index 554bfa3..7bde8ae 100644 --- a/src/lib/backend/convex/conversations.ts +++ b/src/lib/backend/convex/conversations.ts @@ -343,7 +343,7 @@ export const search = query({ .query('messages') .withIndex('by_conversation', (q) => q.eq('conversation_id', conversation._id)) .collect(); - + // Search title const titleResults = enhancedSearch({ needle: args.search_term, @@ -364,12 +364,13 @@ export const search = query({ // If we have matches in title or messages, add to results if (titleResults.length > 0 || messageResults.length > 0) { - const titleScore = titleResults.length > 0 ? titleResults[0]?.score ?? 0 : 0; - const messageScore = messageResults.length > 0 ? Math.max(...messageResults.map(r => r.score)) : 0; - + const titleScore = titleResults.length > 0 ? (titleResults[0]?.score ?? 0) : 0; + const messageScore = + messageResults.length > 0 ? Math.max(...messageResults.map((r) => r.score)) : 0; + results.push({ conversation, - messages: messageResults.map(r => r.item), + messages: messageResults.map((r) => r.item), score: Math.max(titleScore, messageScore), titleMatch: titleResults.length > 0, }); diff --git a/src/lib/backend/convex/messages.ts b/src/lib/backend/convex/messages.ts index ec3c529..7c51387 100644 --- a/src/lib/backend/convex/messages.ts +++ b/src/lib/backend/convex/messages.ts @@ -62,6 +62,9 @@ export const create = mutation({ throw new Error('Unauthorized'); } + const conversation = await ctx.db.get(args.conversation_id as Id<'conversations'>); + if (conversation?.user_id !== session.userId) throw new Error('Unauthorized'); + // I think this just slows us down // const messages = await ctx.runQuery(api.messages.getAllFromConversation, { diff --git a/src/lib/components/app-sidebar.svelte b/src/lib/components/app-sidebar.svelte index d1e4f22..e21f748 100644 --- a/src/lib/components/app-sidebar.svelte +++ b/src/lib/components/app-sidebar.svelte @@ -1,5 +1,6 @@ - -
+ +
+
+ + {#snippet trigger(tooltip)} + + + + {/snippet} + Toggle Sidebar ({cmdOrCtrl} + B) + +
Thom.chat +
-
+
@@ -268,4 +302,4 @@ {/if}
- + diff --git a/src/lib/components/ui/sidebar/sidebar.svelte b/src/lib/components/ui/sidebar/sidebar.svelte index 2404c37..609a561 100644 --- a/src/lib/components/ui/sidebar/sidebar.svelte +++ b/src/lib/components/ui/sidebar/sidebar.svelte @@ -4,9 +4,17 @@ import { useSidebar } from './sidebar.svelte.js'; import { shortcut } from '$lib/actions/shortcut.svelte.js'; - let { children, ...rest }: HTMLAttributes = $props(); + let { + open = $bindable(false), + children, + ...rest + }: HTMLAttributes & { open?: boolean } = $props(); const sidebar = useSidebar(); + + $effect(() => { + open = sidebar.showSidebar; + }); diff --git a/src/lib/components/ui/sidebar/sidebar.svelte.ts b/src/lib/components/ui/sidebar/sidebar.svelte.ts index 7c85f44..9ef455b 100644 --- a/src/lib/components/ui/sidebar/sidebar.svelte.ts +++ b/src/lib/components/ui/sidebar/sidebar.svelte.ts @@ -44,11 +44,16 @@ export class SidebarSidebarState { export class SidebarControlState { constructor(readonly root: SidebarRootState) { this.closeMobile = this.closeMobile.bind(this); + this.toggle = this.toggle.bind(this); } closeMobile() { this.root.closeMobile(); } + + toggle() { + this.root.toggle(); + } } export const ctx = new Context('sidebar-root-context'); diff --git a/src/routes/api/generate-message/call.ts b/src/routes/api/generate-message/call.ts index ae28391..466db37 100644 --- a/src/routes/api/generate-message/call.ts +++ b/src/routes/api/generate-message/call.ts @@ -3,15 +3,25 @@ import type { GenerateMessageRequestBody, GenerateMessageResponse } from './+ser export async function callGenerateMessage(args: GenerateMessageRequestBody) { const res = ResultAsync.fromPromise( - fetch('/api/generate-message', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(args), - }), - (e) => e - ).map((r) => r.json() as Promise); + (async () => { + const res = await fetch('/api/generate-message', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(args), + }); + + if (!res.ok) { + const { message } = await res.json(); + + throw new Error(message as string); + } + + return res.json() as Promise; + })(), + (e) => `${e}` + ); return res; } diff --git a/src/routes/chat/+layout.svelte b/src/routes/chat/+layout.svelte index 18a1b67..0d831b4 100644 --- a/src/routes/chat/+layout.svelte +++ b/src/routes/chat/+layout.svelte @@ -32,25 +32,25 @@ import CheckIcon from '~icons/lucide/check'; import ChevronDownIcon from '~icons/lucide/chevron-down'; import ImageIcon from '~icons/lucide/image'; - import LockIcon from '~icons/lucide/lock'; - import LockOpenIcon from '~icons/lucide/lock-open'; import PanelLeftIcon from '~icons/lucide/panel-left'; - import SearchIcon from '~icons/lucide/search'; import Settings2Icon from '~icons/lucide/settings-2'; - import ShareIcon from '~icons/lucide/share'; - import StopIcon from '~icons/lucide/square'; import UploadIcon from '~icons/lucide/upload'; import XIcon from '~icons/lucide/x'; - import { callCancelGeneration } from '../api/cancel-generation/call.js'; + import SearchIcon from '~icons/lucide/search'; import { callGenerateMessage } from '../api/generate-message/call.js'; + import { callCancelGeneration } from '../api/cancel-generation/call.js'; import ModelPicker from './model-picker.svelte'; + import ShareIcon from '~icons/lucide/share'; + import { fade } from 'svelte/transition'; + import LockIcon from '~icons/lucide/lock'; + import LockOpenIcon from '~icons/lucide/lock-open'; + import StopIcon from '~icons/lucide/square'; import SearchModal from './search-modal.svelte'; const client = useConvexClient(); let { children } = $props(); - let form = $state(); let textarea = $state(); let abortController = $state(null); @@ -89,11 +89,20 @@ let loading = $state(false); - const textareaDisabled = $derived(isGenerating || loading); + const textareaDisabled = $derived( + isGenerating || + loading || + (currentConversationQuery.data && + currentConversationQuery.data.user_id !== session.current?.user.id) + ); + + let error = $state(null); async function handleSubmit() { if (isGenerating) return; + error = null; + // TODO: Re-use zod here from server endpoint for better error messages? if (message.current === '' || !session.current?.user.id || !settings.modelId) return; @@ -113,7 +122,8 @@ }); if (res.isErr()) { - return; // TODO: Handle error + error = res._unsafeUnwrapErr() ?? 'An unknown error occurred'; + return; } const cid = res.value.conversation_id; @@ -384,6 +394,14 @@ () => !scrollState.arrived.bottom, () => (mounted.current ? 250 : 0) ); + + let searchModalOpen = $state(false); + + function openSearchModal() { + searchModalOpen = true; + } + + let sidebarOpen = $state(false); @@ -391,41 +409,58 @@ - + - - {#snippet trigger(tooltip)} - - - - {/snippet} - {cmdOrCtrl} + B - - - {#if page.params.id} + +
{#snippet trigger(tooltip)} -
- {#if currentConversationQuery.data?.public} - - {:else} - - {/if} -
+ + + {/snippet} - {currentConversationQuery.data?.public ? 'Public' : 'Private'} + Toggle Sidebar ({cmdOrCtrl} + B)
- {/if} - -
+ {#if page.params.id} + + {#snippet trigger(tooltip)} +
+ {#if currentConversationQuery.data?.public} + + {:else} + + {/if} +
+ {/snippet} + {currentConversationQuery.data?.public ? 'Public' : 'Private'} +
+ {/if} +
+ + +
{#if page.params.id} {#snippet trigger(tooltip)} @@ -454,7 +489,22 @@ Share {/if} - + + {#snippet trigger(tooltip)} + + {/snippet} + Search ({cmdOrCtrl} + K) + + {#snippet trigger(tooltip)} - {/snippet} - Search ({cmdOrCtrl} + K) - -

Search Conversations

{ if (!open) return; setTimeout(() => { - console.log('focus', node, open); if (open) node.focus(); }, 50); }}