@@ -23,9 +23,11 @@ import { cn } from '~/lib/utils'
23
23
import { AiIconAnimation } from './ai-icon-animation'
24
24
import { useApp } from './app-provider'
25
25
import ChatMessage from './chat-message'
26
+ import { CopyableField } from './copyable-field'
26
27
import SignInButton from './sign-in-button'
28
+ import { Accordion , AccordionContent , AccordionItem , AccordionTrigger } from './ui/accordion'
29
+ import { Tabs , TabsContent , TabsList , TabsTrigger } from './ui/tabs'
27
30
import { useWorkspace } from './workspace'
28
- import { CopyableField } from './copyable-field'
29
31
30
32
export function getInitialMessages ( tables : TablesData ) : Message [ ] {
31
33
return [
@@ -244,49 +246,7 @@ export default function Chat() {
244
246
) }
245
247
ref = { scrollRef }
246
248
>
247
- { liveShare . isLiveSharing && (
248
- < div className = "h-full w-full max-w-4xl flex flex-col gap-10 p-10 absolute backdrop-blur-sm bg-card/90 z-10" >
249
- < div className = "flex items-center justify-center h-full flex-col gap-y-5" >
250
- < div className = "w-full text-left" >
251
- < p className = "text-lg" > Access your in-browser database</ p >
252
- < p className = "text-xs text-muted-foreground" >
253
- Closing the window will stop the Live Share session
254
- </ p >
255
- </ div >
256
- < CopyableField
257
- value = { `postgres://postgres@${ liveShare . databaseId } .${ process . env . NEXT_PUBLIC_BROWSER_PROXY_DOMAIN } /postgres?sslmode=require` }
258
- />
259
-
260
- { liveShare . clientIp ? (
261
- < p className = "text-sm text-muted-foreground flex items-center gap-2" >
262
- < span className = "relative flex h-3 w-3" >
263
- < span className = "animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" > </ span >
264
- < span className = "relative inline-flex rounded-full h-3 w-3 bg-primary" > </ span >
265
- </ span >
266
- < span >
267
- Connected from{ ' ' }
268
- < span className = "text-card-foreground" > { liveShare . clientIp } </ span >
269
- </ span >
270
- </ p >
271
- ) : (
272
- < p className = "text-sm text-muted-foreground flex items-center gap-2" >
273
- < span className = "relative inline-flex rounded-full h-3 w-3 bg-muted-foreground" > </ span >
274
- < span > Not connected</ span >
275
- </ p >
276
- ) }
277
- < Button
278
- className = "w-full gap-2"
279
- variant = "outline"
280
- onClick = { ( ) => {
281
- liveShare . stop ( )
282
- } }
283
- >
284
- < PlugIcon size = { 16 } />
285
- < span > Stop sharing database</ span >
286
- </ Button >
287
- </ div >
288
- </ div >
289
- ) }
249
+ < LiveShareOverlay databaseId = { databaseId } />
290
250
< m . div
291
251
key = { databaseId }
292
252
className = "flex flex-col gap-4 w-full max-w-4xl p-10"
@@ -374,18 +334,21 @@ export default function Chat() {
374
334
) : (
375
335
< div className = "h-full w-full max-w-4xl flex flex-col gap-10 justify-center items-center" >
376
336
{ user ? (
377
- < m . h3
378
- layout
379
- className = "text-2xl font-light text-center"
380
- variants = { {
381
- hidden : { opacity : 0 , y : 10 } ,
382
- show : { opacity : 1 , y : 0 } ,
383
- } }
384
- initial = "hidden"
385
- animate = "show"
386
- >
387
- What would you like to create?
388
- </ m . h3 >
337
+ < >
338
+ < LiveShareOverlay databaseId = { databaseId } />
339
+ < m . h3
340
+ layout
341
+ className = "text-2xl font-light text-center"
342
+ variants = { {
343
+ hidden : { opacity : 0 , y : 10 } ,
344
+ show : { opacity : 1 , y : 0 } ,
345
+ } }
346
+ initial = "hidden"
347
+ animate = "show"
348
+ >
349
+ What would you like to create?
350
+ </ m . h3 >
351
+ </ >
389
352
) : (
390
353
< m . div
391
354
className = "flex flex-col items-center gap-4 max-w-lg"
@@ -570,3 +533,182 @@ export default function Chat() {
570
533
</ div >
571
534
)
572
535
}
536
+
537
+ function LiveShareOverlay ( props : { databaseId : string } ) {
538
+ const { liveShare } = useApp ( )
539
+
540
+ if ( liveShare . isLiveSharing && liveShare . databaseId === props . databaseId ) {
541
+ return (
542
+ < div className = "h-full w-full max-w-4xl absolute flex flex-col items-stretch justify-center backdrop-blur-sm bg-card/90 z-10" >
543
+ < div className = "flex flex-col items-center justify-start gap-y-7 overflow-y-auto pt-20 pb-10" >
544
+ < div className = "w-full text-left" >
545
+ < p className = "text-lg" > Access your in-browser database</ p >
546
+ < p className = "text-xs text-muted-foreground" >
547
+ Closing the window will stop the Live Share session
548
+ </ p >
549
+ </ div >
550
+ < Tabs defaultValue = "uri" className = "w-full justify-between bg-muted/50 rounded-md border" >
551
+ < TabsList className = "w-full flex justify-start bg-transparent px-3" >
552
+ < TabsTrigger
553
+ value = "uri"
554
+ className = "hover:text-foreground data-[state=active]:border-b-2 data-[state=active]:border-foreground data-[state=active]:bg-none! data-[state=active]:text-foreground data-[state=active]:shadow-none rounded-none relative cursor-pointer text-foreground-lighter flex items-center space-x-2 text-center transition focus:outline-none focus-visible:ring focus-visible:ring-foreground-muted focus-visible:border-foreground-muted text-xs px-2.5 py-1"
555
+ >
556
+ URI
557
+ </ TabsTrigger >
558
+ < TabsTrigger
559
+ value = "psql"
560
+ className = "hover:text-foreground data-[state=active]:border-b-2 data-[state=active]:border-foreground data-[state=active]:bg-none! data-[state=active]:text-foreground data-[state=active]:shadow-none rounded-none relative cursor-pointer text-foreground-lighter flex items-center space-x-2 text-center transition focus:outline-none focus-visible:ring focus-visible:ring-foreground-muted focus-visible:border-foreground-muted text-xs px-2.5 py-1"
561
+ >
562
+ PSQL
563
+ </ TabsTrigger >
564
+ </ TabsList >
565
+ < TabsContent value = "uri" className = "px-2 pb-2" >
566
+ < CopyableField
567
+ value = { `postgres://postgres@${ liveShare . databaseId } .${ process . env . NEXT_PUBLIC_BROWSER_PROXY_DOMAIN } /postgres?sslmode=require` }
568
+ />
569
+ </ TabsContent >
570
+ < TabsContent value = "psql" className = "px-2 pb-2" >
571
+ < CopyableField
572
+ value = { `psql "postgres://postgres@${ liveShare . databaseId } .${ process . env . NEXT_PUBLIC_BROWSER_PROXY_DOMAIN } /postgres?sslmode=require"` }
573
+ />
574
+ </ TabsContent >
575
+ </ Tabs >
576
+
577
+ { liveShare . clientIp ? (
578
+ < p className = "text-sm text-muted-foreground flex items-center gap-2" >
579
+ < span className = "relative flex h-3 w-3" >
580
+ < span className = "animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" > </ span >
581
+ < span className = "relative inline-flex rounded-full h-3 w-3 bg-primary" > </ span >
582
+ </ span >
583
+ < span >
584
+ Connected from < span className = "text-card-foreground" > { liveShare . clientIp } </ span >
585
+ </ span >
586
+ </ p >
587
+ ) : (
588
+ < p className = "text-sm text-muted-foreground flex items-center gap-2" >
589
+ < span className = "relative inline-flex rounded-full h-3 w-3 bg-muted-foreground" > </ span >
590
+ < span > No client connected</ span >
591
+ </ p >
592
+ ) }
593
+ < Button
594
+ className = "w-full gap-2"
595
+ variant = "outline"
596
+ onClick = { ( ) => {
597
+ liveShare . stop ( )
598
+ } }
599
+ >
600
+ < PlugIcon size = { 16 } />
601
+ < span > Stop sharing database</ span >
602
+ </ Button >
603
+
604
+ < div className = "self-stretch border-b border-b-foreground/15 my-4" />
605
+
606
+ < Accordion type = "single" collapsible className = "self-stretch flex flex-col gap-2" >
607
+ < AccordionItem value = "postgres-clients" className = "border rounded-md" >
608
+ < AccordionTrigger className = "p-0 gap-2 px-3 py-2" >
609
+ < div className = "flex gap-2 items-center font-normal text-lighter text-sm" >
610
+ < span > Can I connect using any Postgres client?</ span >
611
+ </ div >
612
+ </ AccordionTrigger >
613
+ < AccordionContent className = "p-3 prose prose-sm" >
614
+ < p >
615
+ Yes! Any standard Postgres client that communicates using the Postgres wire
616
+ protocol is supported. Connections are established over an encrypted TLS channel
617
+ using the SNI extension, so your client will also need to support TLS with SNI
618
+ (most modern clients support this).
619
+ </ p >
620
+ </ AccordionContent >
621
+ </ AccordionItem >
622
+ < AccordionItem value = "concurrent-connections" className = "border rounded-md" >
623
+ < AccordionTrigger className = "p-0 gap-2 px-3 py-2" >
624
+ < div className = "flex gap-2 items-center font-normal text-lighter text-sm" >
625
+ < span > How many concurrent connections can I have?</ span >
626
+ </ div >
627
+ </ AccordionTrigger >
628
+ < AccordionContent className = "p-3 prose prose-sm" >
629
+ < p >
630
+ PGlite operates in single-user mode, so you can only establish one connection at a
631
+ time per database. If you attempt to establish more than one connection using the
632
+ Live Share connection string, you will receive a "too many clients"
633
+ error.
634
+ </ p >
635
+ </ AccordionContent >
636
+ </ AccordionItem >
637
+ < AccordionItem value = "orms" className = "border rounded-md" >
638
+ < AccordionTrigger className = "p-0 gap-2 px-3 py-2" >
639
+ < div className = "flex gap-2 items-center font-normal text-lighter text-sm" >
640
+ < span > Does this work with ORMs?</ span >
641
+ </ div >
642
+ </ AccordionTrigger >
643
+ < AccordionContent className = "p-3 prose prose-sm" >
644
+ < p >
645
+ Yes, as long as your ORM doesn't require multiple concurrent connections.
646
+ Some ORMs like Prisma run a shadow database in parallel to your main database in
647
+ order to generate migrations. In order to use Prisma, you will need to{ ' ' }
648
+ < a
649
+ href = "https://www.prisma.io/docs/orm/prisma-migrate/understanding-prisma-migrate/shadow-database#manually-configuring-the-shadow-database"
650
+ target = "__blank"
651
+ rel = "noopener noreferrer"
652
+ >
653
+ manually configure
654
+ </ a > { ' ' }
655
+ your shadow database to point to another temporary database.
656
+ </ p >
657
+ </ AccordionContent >
658
+ </ AccordionItem >
659
+ < AccordionItem value = "connection-length" className = "border rounded-md" >
660
+ < AccordionTrigger className = "p-0 gap-2 px-3 py-2" >
661
+ < div className = "flex gap-2 items-center font-normal text-lighter text-sm" >
662
+ < span > How long will connections last?</ span >
663
+ </ div >
664
+ </ AccordionTrigger >
665
+ < AccordionContent className = "p-3 prose prose-sm" >
666
+ < p >
667
+ You can connect over Live Share for as long as your browser tab is active. Once
668
+ your tab is closed, the any existing connection will terminate and you will no
669
+ longer be able to connect to your database using the connection string.
670
+ </ p >
671
+ < p >
672
+ To prevent overloading the system, we also enforce a 5 minute idle timeout per
673
+ client connection and 1 hour total timeout per database. If you need to
674
+ communicate longer than these limits, simply reconnect after the timeout.
675
+ </ p >
676
+ </ AccordionContent >
677
+ </ AccordionItem >
678
+ < AccordionItem value = "under-the-hood" className = "border rounded-md" >
679
+ < AccordionTrigger className = "p-0 gap-2 px-3 py-2" >
680
+ < div className = "flex gap-2 items-center font-normal text-lighter text-sm" >
681
+ < span > How does this work under the hood?</ span >
682
+ </ div >
683
+ </ AccordionTrigger >
684
+ < AccordionContent className = "p-3 prose prose-sm" >
685
+ < p >
686
+ We host a{ ' ' }
687
+ < a
688
+ href = "https://github.com/supabase-community/postgres-new/tree/main/apps/browser-proxy"
689
+ target = "__blank"
690
+ rel = "noopener noreferrer"
691
+ >
692
+ lightweight proxy
693
+ </ a > { ' ' }
694
+ between your Postgres client and your in-browser PGlite database. It uses{ ' ' }
695
+ < a
696
+ href = "https://github.com/supabase-community/pg-gateway"
697
+ target = "__blank"
698
+ rel = "noopener noreferrer"
699
+ >
700
+ pg-gateway
701
+ </ a > { ' ' }
702
+ to proxy inbound TCP connections to the corresponding browser instance via a Web
703
+ Socket reverse tunnel.
704
+ </ p >
705
+ </ AccordionContent >
706
+ </ AccordionItem >
707
+ </ Accordion >
708
+ </ div >
709
+ </ div >
710
+ )
711
+
712
+ return null
713
+ }
714
+ }
0 commit comments