feat: add widget app — chat screens, VAPI integration, auth, voice UI - #6
Conversation
|
Warning Review limit reached
Next review available in: 58 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Free Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (21)
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
There was a problem hiding this comment.
Pull request overview
This PR introduces a standalone “widget” app with an embeddable launcher script and a multi-screen UI for authenticated chat, inbox browsing, and optional voice calling via Vapi.
Changes:
- Added a public embed script (
widget.js) that injects a floating button and an iframe pointing at the widget app. - Implemented widget UI flow (loading → auth/selection → chat/inbox/voice/contact) using Jotai state and Convex queries/actions.
- Added Vapi integration (voice calls + transcript UI) and supporting dependencies/assets.
Reviewed changes
Copilot reviewed 19 out of 22 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/widget/public/widget.js | Embeddable script that renders the launcher button + iframe and handles postMessage events. |
| apps/widget/public/logo.svg | Logo asset used for assistant/avatar UI. |
| apps/widget/package.json | Adds dependencies for Vapi, forms, validation, and supporting utilities. |
| apps/widget/modules/widget/ui/views/widget-view.tsx | Screen router that renders the active widget screen based on atom state. |
| apps/widget/modules/widget/ui/screens/widget-loading-screen.tsx | Initialization flow: org validation, session validation, settings load, Vapi secrets load. |
| apps/widget/modules/widget/ui/screens/widget-auth-screen.tsx | Contact-session creation form (name/email) and metadata capture. |
| apps/widget/modules/widget/ui/screens/widget-selection-screen.tsx | Entry point for starting chat, voice, or phone contact (feature-gated by settings/secrets). |
| apps/widget/modules/widget/ui/screens/widget-chat-screen.tsx | Chat UI powered by Convex agent threads, suggestions, and message composer. |
| apps/widget/modules/widget/ui/screens/widget-inbox-screen.tsx | Paginated list of past conversations with infinite scroll. |
| apps/widget/modules/widget/ui/screens/widget-voice-screen.tsx | Voice call UI with live transcript display and speaking/listening indicator. |
| apps/widget/modules/widget/ui/screens/widget-contact-screen.tsx | Shows phone number, copy-to-clipboard, and “Call Now” action. |
| apps/widget/modules/widget/ui/screens/widget-error-screen.tsx | Error display screen for invalid configuration/init failures. |
| apps/widget/modules/widget/ui/components/widget-header.tsx | Shared header with theme toggle. |
| apps/widget/modules/widget/ui/components/widget-footer.tsx | Shared footer navigation (home/inbox). |
| apps/widget/modules/widget/hooks/use-vapi.ts | Vapi client hook: connect/disconnect, speaking state, transcript events. |
| apps/widget/modules/widget/atoms/widget-atoms.ts | Jotai atoms for screen routing, org/session IDs, settings, and secrets. |
| apps/widget/modules/widget/constants.ts | Screen constants and contact-session storage key prefix. |
| apps/widget/modules/widget/types.ts | Type helper for widget screen union. |
| apps/widget/app/page.tsx | Widget app entry page that passes organizationId into the UI. |
| apps/widget/app/layout.tsx | Minor formatting change. |
| apps/widget/lib/.gitkeep | Keeps lib directory in git. |
| apps/widget/hooks/.gitkeep | Keeps hooks directory in git. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,43 @@ | |||
| (function(){"use strict";const o={WIDGET_URL:"localhost:3000",DEFAULT_POSITION:"bottom-right"},u=`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="white" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |||
| "use client"; | ||
| import {useMutation, useQuery} from 'convex/react'; | ||
| import {api} from "@workspace/backend/_generated/api"; | ||
| import { Button } from '@workspace/ui/components/button'; | ||
|
|
||
| export default function Page() { | ||
| const users = useQuery(api.users.getMany); | ||
| const addUser = useMutation(api.users.add); | ||
| import { use } from "react"; | ||
|
|
||
| import { WidgetView } from "@/modules/widget/ui/views/widget-view"; |
| useEffect(() => { | ||
| if (!vapiSecrets) { | ||
| return; | ||
| } | ||
|
|
| return () => { | ||
| vapiInstance.disconnect(); | ||
| }; |
| return () => { | ||
| vapiInstance.disconnect(); | ||
| }; | ||
| }, []); |
| import { | ||
| AIConversation, | ||
| AIConversationContent, | ||
| AIConversationScrollButton, | ||
| } from "@workspace/ui/components/ai/conversation"; |
| const form = useForm<z.infer<typeof formSchema>>({ | ||
| resolver: zodResolver(formSchema), | ||
| defaultValues: { | ||
| message: "", | ||
| }, | ||
| }); |
| <Button asChild className="w-full" size="lg"> | ||
| <Link href={`tel:${phoneNumber}`}> | ||
| <PhoneIcon /> | ||
| Call Now | ||
| </Link> | ||
| </Button> |
No description provided.