Skip to content

Commit ababd4d

Browse files
committed
refactor: enhance Composer component with API key validation and settings integration
1 parent a695090 commit ababd4d

1 file changed

Lines changed: 66 additions & 15 deletions

File tree

app/components/assistant-ui/thread.tsx

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ChevronLeftIcon,
1414
ChevronRightIcon,
1515
CopyIcon,
16+
LockIcon,
1617
PencilIcon,
1718
RefreshCwIcon,
1819
Square,
@@ -29,6 +30,7 @@ import { SettingsDialog } from "@/app/components/assistant-ui/SettingsDialog";
2930
import { MarkdownText } from "@/app/components/assistant-ui/markdown-text";
3031
import { ToolFallback } from "@/app/components/assistant-ui/tool-fallback";
3132
import { TooltipIconButton } from "@/app/components/assistant-ui/tooltip-icon-button";
33+
import { useAssistantSettings } from "@/app/hooks/useAssistantSettings";
3234
import { Button } from "@/components/ui/button";
3335
import { cn } from "@/lib/utils";
3436
import { LazyMotion, MotionConfig, domAnimation } from "motion/react";
@@ -168,49 +170,101 @@ const ThreadWelcomeSuggestions: FC = () => {
168170
};
169171

170172
const Composer: FC = () => {
173+
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
174+
const { provider, openaiApiKey, geminiApiKey } = useAssistantSettings();
175+
const activeKey = provider === "openai" ? openaiApiKey : geminiApiKey;
176+
const hasActiveKey = activeKey.trim().length > 0;
177+
const providerLabel = provider === "gemini" ? "Google Gemini" : "OpenAI";
178+
171179
return (
172180
<div className="aui-composer-wrapper sticky bottom-0 mx-auto flex w-full max-w-[var(--thread-max-width)] flex-col gap-4 overflow-visible rounded-t-3xl pb-4 md:pb-6">
173181
<ThreadScrollToBottom />
174182
<ThreadPrimitive.Empty>
175183
<ThreadWelcomeSuggestions />
176184
</ThreadPrimitive.Empty>
177-
<ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col rounded-3xl border border-border bg-muted px-1 pt-2 shadow-[0_9px_9px_0px_rgba(0,0,0,0.01),0_2px_5px_0px_rgba(0,0,0,0.06)] dark:border-muted-foreground/15">
185+
<ComposerPrimitive.Root
186+
className="aui-composer-root relative flex w-full flex-col rounded-3xl border border-border bg-muted px-1 pt-2 shadow-[0_9px_9px_0px_rgba(0,0,0,0.01),0_2px_5px_0px_rgba(0,0,0,0.06)] dark:border-muted-foreground/15"
187+
aria-disabled={!hasActiveKey}
188+
data-key-required={!hasActiveKey}
189+
>
190+
{!hasActiveKey && (
191+
<div className="absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 rounded-3xl bg-background/90 px-6 text-center backdrop-blur-sm">
192+
<p className="text-sm text-muted-foreground">
193+
Add your {providerLabel} API key in Settings to start chatting.
194+
</p>
195+
<Button
196+
type="button"
197+
size="sm"
198+
onClick={() => setIsSettingsOpen(true)}
199+
>
200+
Open settings
201+
</Button>
202+
</div>
203+
)}
178204
<ComposerAttachments />
179205
<ComposerPrimitive.Input
180206
placeholder="Send a message..."
181207
className="aui-composer-input mb-1 max-h-32 min-h-16 w-full resize-none bg-transparent px-3.5 pt-1.5 pb-3 text-base outline-none placeholder:text-muted-foreground focus:outline-primary"
182208
rows={1}
183209
autoFocus
184210
aria-label="Message input"
211+
disabled={!hasActiveKey}
212+
/>
213+
<ComposerAction
214+
canSend={hasActiveKey}
215+
isSettingsOpen={isSettingsOpen}
216+
onOpenChange={setIsSettingsOpen}
185217
/>
186-
<ComposerAction />
187218
</ComposerPrimitive.Root>
188219
</div>
189220
);
190221
};
191222

192-
const ComposerAction: FC = () => {
193-
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
223+
interface ComposerActionProps {
224+
canSend: boolean;
225+
isSettingsOpen: boolean;
226+
onOpenChange: (open: boolean) => void;
227+
}
194228

229+
const ComposerAction: FC<ComposerActionProps> = ({
230+
canSend,
231+
isSettingsOpen,
232+
onOpenChange,
233+
}) => {
195234
return (
196235
<>
197236
<div className="aui-composer-action-wrapper relative mx-1 mt-2 mb-2 flex items-center justify-between">
198-
<SettingsButton onClick={() => setIsSettingsOpen(true)} />
237+
<SettingsButton onClick={() => onOpenChange(true)} />
199238

200239
<ThreadPrimitive.If running={false}>
201-
<ComposerPrimitive.Send asChild>
240+
{canSend ? (
241+
<ComposerPrimitive.Send asChild>
242+
<TooltipIconButton
243+
tooltip="Send message"
244+
side="bottom"
245+
type="submit"
246+
variant="default"
247+
size="icon"
248+
className="aui-composer-send size-[34px] rounded-full p-1"
249+
aria-label="Send message"
250+
>
251+
<ArrowUpIcon className="aui-composer-send-icon size-5" />
252+
</TooltipIconButton>
253+
</ComposerPrimitive.Send>
254+
) : (
202255
<TooltipIconButton
203-
tooltip="Send message"
256+
tooltip="Configure an API key to enable sending"
204257
side="bottom"
205-
type="submit"
258+
type="button"
206259
variant="default"
207260
size="icon"
208261
className="aui-composer-send size-[34px] rounded-full p-1"
209-
aria-label="Send message"
262+
aria-label="Open assistant settings"
263+
onClick={() => onOpenChange(true)}
210264
>
211-
<ArrowUpIcon className="aui-composer-send-icon size-5" />
265+
<LockIcon className="aui-composer-send-icon size-5" />
212266
</TooltipIconButton>
213-
</ComposerPrimitive.Send>
267+
)}
214268
</ThreadPrimitive.If>
215269

216270
<ThreadPrimitive.If running>
@@ -227,10 +281,7 @@ const ComposerAction: FC = () => {
227281
</ComposerPrimitive.Cancel>
228282
</ThreadPrimitive.If>
229283
</div>
230-
<SettingsDialog
231-
isOpen={isSettingsOpen}
232-
onOpenChange={setIsSettingsOpen}
233-
/>
284+
<SettingsDialog isOpen={isSettingsOpen} onOpenChange={onOpenChange} />
234285
</>
235286
);
236287
};

0 commit comments

Comments
 (0)