@@ -27,10 +27,12 @@ import Link from "next/link"
2727
2828import  {  Index  }  from  "@/__registry__" 
2929import  {  getIconForLanguageExtension  }  from  "@/components/icons" 
30+ import  {  siteConfig  }  from  "@/config/site" 
3031import  {  useCopyToClipboard  }  from  "@/hooks/use-copy-to-clipboard" 
3132import  {  trackEvent  }  from  "@/lib/events" 
33+ import  {  cn  }  from  "@/lib/utils" 
3234
33- import  {  Button  }  from  "./ui/button" 
35+ import  {  Button ,   buttonVariants  }  from  "./ui/button" 
3436import  {  ResizableHandle ,  ResizablePanel ,  ResizablePanelGroup  }  from  "./ui/resizable" 
3537import  {  Sidebar ,  SidebarGroup ,  SidebarGroupContent ,  SidebarGroupLabel ,  SidebarMenu ,  SidebarMenuButton ,  SidebarMenuItem ,  SidebarMenuSub ,  SidebarProvider  }  from  "./ui/sidebar" 
3638import  {  Tabs ,  TabsList ,  TabsTrigger  }  from  "./ui/tabs" 
@@ -49,6 +51,7 @@ type BlockViewerContext = {
4951  view : "code"  |  "preview" 
5052  setActiveFile : ( file : string )  =>  void 
5153  setView : ( view : "code"  |  "preview" )  =>  void 
54+   blocks ?: boolean 
5255  iframeKey ?: number 
5356  setIframeKey ?: React . Dispatch < React . SetStateAction < number > > 
5457} 
@@ -64,11 +67,12 @@ function useBlockViewer() {
6467} 
6568
6669function  BlockViewerProvider ( { 
70+   blocks, 
6771  children, 
6872  highlightedFiles, 
6973  item, 
7074  tree, 
71- } : Pick < BlockViewerContext ,  "highlightedFiles"  |  "item"  |  "tree" >  &  { 
75+ } : Pick < BlockViewerContext ,  "blocks"    |   " highlightedFiles"|  "item"  |  "tree" >  &  { 
7276  children : React . ReactNode 
7377} )  { 
7478  const  [ view ,  setView ]  =  React . useState < BlockViewerContext [ "view" ] > ( "preview" ) 
@@ -82,6 +86,7 @@ function BlockViewerProvider({
8286    < BlockViewerContext . Provider 
8387      value = { { 
8488        activeFile, 
89+         blocks, 
8590        highlightedFiles, 
8691        iframeKey, 
8792        item, 
@@ -110,111 +115,156 @@ function BlockViewerProvider({
110115} 
111116
112117function  BlockViewerToolbar ( )  { 
113-   const  {  item,  resizablePanelRef,  setIframeKey,  setView,  view }  = 
118+   const  {  blocks ,   item,  resizablePanelRef,  setIframeKey,  setView,  view }  = 
114119    useBlockViewer ( ) 
120+ 
121+ 
115122  const  {  copyToClipboard,  isCopied }  =  useCopyToClipboard ( ) 
116123
124+   const  isPro  =  item ?. meta ?. isPro 
125+ 
117126  return  ( 
118-     < div  className = "hidden w-full items-center gap-2 pl-2 md:pr-6 lg:flex" > 
127+     < div  className = "hidden w-full items-center gap-2 pl-2 py-1  md:pr-6 lg:flex" > 
119128      < Tabs 
120129        value = { view } 
121130        onValueChange = { ( value )  =>  setView ( value  as  "code"  |  "preview" ) } 
122131      > 
123-         < TabsList  className = "grid h-8 grid-cols-2 items-center rounded-md p-1 *:data-[slot=tabs-trigger]:h-6 *:data-[slot=tabs-trigger]:rounded-sm *:data-[slot=tabs-trigger]:px-2 *:data-[slot=tabs-trigger]:text-xs" > 
132+         < TabsList  className = { cn ( "grid h-8 items-center rounded-md p-1 *:data-[slot=tabs-trigger]:h-6 *:data-[slot=tabs-trigger]:rounded-sm *:data-[slot=tabs-trigger]:px-2 *:data-[slot=tabs-trigger]:text-xs" , 
133+           isPro  ? "grid-cols-1"  : "grid-cols-2" 
134+         ) } > 
124135          < TabsTrigger  value = "preview" > Preview</ TabsTrigger > 
125-           < TabsTrigger  value = "code" > Code</ TabsTrigger > 
136+           { ! isPro   &&   < TabsTrigger  value = "code" > Code</ TabsTrigger > } 
126137        </ TabsList > 
127138      </ Tabs > 
128139      < Separator  orientation = "vertical"  className = "mx-2 !h-4"  /> 
129-       < a 
130-         className = "flex-1 text-center text-sm font-medium underline-offset-2 hover:underline md:flex-auto md:text-left" 
131-         href = { `#${ item . name }  } 
132-       > 
133-         { item . description ?. replace ( / \. $ / ,  "" ) } 
134-       </ a > 
135-       < div  className = "ml-auto flex items-center gap-2" > 
136-         < div  className = "h-8 items-center gap-1.5 rounded-md border p-1 shadow-none" > 
137-           < ToggleGroup 
138-             className = "gap-1 *:data-[slot=toggle-group-item]:!size-6 *:data-[slot=toggle-group-item]:!rounded-sm" 
139-             defaultValue = "100" 
140-             onValueChange = { ( value )  =>  { 
141-               setView ( "preview" ) 
142-               if  ( resizablePanelRef ?. current )  { 
143-                 resizablePanelRef . current . resize ( Number . parseInt ( value ) ) 
144-               } 
145-             } } 
146-             type = "single" 
140+ 
141+       { 
142+         blocks  &&  ( 
143+           < a 
144+             className = "flex-1 text-center text-sm font-medium underline-offset-2 hover:underline md:flex-auto md:text-left" 
145+             href = { `#${ item . name }  } 
147146          > 
148-             < ToggleGroupItem  value = "100"  title = "Desktop" > 
149-               < Monitor  /> 
150-             </ ToggleGroupItem > 
151-             < ToggleGroupItem  value = "60"  title = "Tablet" > 
152-               < Tablet  /> 
153-             </ ToggleGroupItem > 
154-             < ToggleGroupItem  value = "30"  title = "Mobile" > 
155-               < Smartphone  /> 
156-             </ ToggleGroupItem > 
157-             < Separator  orientation = "vertical"  className = "!h-4"  /> 
158-             < Button 
159-               asChild 
160-               size = "icon" 
161-               variant = "ghost" 
162-               className = "size-6 rounded-sm p-0" 
163-               title = "Open in New Tab" 
164-             > 
165-               < Link  href = { `/blocks/${ item . name }  }  target = "_blank" > 
166-                 < span  className = "sr-only" > Open in New Tab</ span > 
167-                 < Fullscreen  /> 
168-               </ Link > 
169-             </ Button > 
170-             < Separator  orientation = "vertical"  className = "!h-4"  /> 
171-             < Button 
172-               size = "icon" 
173-               variant = "ghost" 
174-               className = "size-6 rounded-sm p-0" 
175-               onClick = { ( )  =>  { 
176-                 if  ( setIframeKey )  { 
177-                   setIframeKey ( ( k )  =>  k  +  1 ) 
178-                 } 
179-               } } 
180-               title = "Refresh Preview" 
181-             > 
182-               < RotateCw  /> 
183-               < span  className = "sr-only" > Refresh Preview</ span > 
184-             </ Button > 
185-           </ ToggleGroup > 
186-         </ div > 
187-         < Separator  orientation = "vertical"  className = "mx-1 !h-4"  /> 
188-         < Button 
189-           size = "sm" 
190-           variant = "outline" 
191-           className = "w-fit gap-1 px-2 shadow-none" 
192-           onClick = { ( )  =>  { 
193-             copyToClipboard ( `npx shadcn@latest add ${ item . name }  ) 
194-           } } 
147+             { item . description ?. replace ( / \. $ / ,  "" ) } 
148+           </ a > 
149+         ) 
150+       } 
151+ 
152+       { isPro  ? ( 
153+         < Link 
154+           className = { cn ( 
155+             buttonVariants ( ) , 
156+             'group relative flex justify-start gap-2 overflow-hidden rounded-sm whitespace-pre' , 
157+             'dark:bg-muted dark:text-foreground' , 
158+             'hover:ring-2 hover:ring-primary hover:ring-offset-2' , 
159+             'transition-all duration-300 ease-out' , 
160+             'h-[26px] px-2 text-xs' 
161+           ) } 
162+           href = { item . meta ?. descriptionSrc  ??  siteConfig . links . potionIframe } 
163+           target = "_blank" 
195164        > 
196-           { isCopied  ? < Check  />  : < Terminal  /> } 
197-           < span > npx shadcn@latest add { item . name } </ span > 
198-         </ Button > 
199-         < Separator  orientation = "vertical"  className = "mx-1 !h-4"  /> 
200-       </ div > 
165+           < span 
166+             className = { cn ( 
167+               'absolute right-0 -mt-12 h-32 w-8 translate-x-12 rotate-12' , 
168+               'bg-white opacity-10' , 
169+               'transition-all duration-1000 ease-out' 
170+             ) } 
171+           /> 
172+           Get the code
173+         </ Link > 
174+       )  :
175+         ( 
176+           < div  className = "ml-auto flex items-center gap-2" > 
177+             { 
178+               blocks  &&  ( 
179+                 < React . Fragment > 
180+                   < div  className = "h-8 items-center gap-1.5 rounded-md border p-1 shadow-none" > 
181+                     < ToggleGroup 
182+                       className = "gap-1 *:data-[slot=toggle-group-item]:!size-6 *:data-[slot=toggle-group-item]:!rounded-sm" 
183+                       defaultValue = "100" 
184+                       onValueChange = { ( value )  =>  { 
185+                         setView ( "preview" ) 
186+                         if  ( resizablePanelRef ?. current )  { 
187+                           resizablePanelRef . current . resize ( Number . parseInt ( value ) ) 
188+                         } 
189+                       } } 
190+                       type = "single" 
191+                     > 
192+                       < ToggleGroupItem  value = "100"  title = "Desktop" > 
193+                         < Monitor  /> 
194+                       </ ToggleGroupItem > 
195+                       < ToggleGroupItem  value = "60"  title = "Tablet" > 
196+                         < Tablet  /> 
197+                       </ ToggleGroupItem > 
198+                       < ToggleGroupItem  value = "30"  title = "Mobile" > 
199+                         < Smartphone  /> 
200+                       </ ToggleGroupItem > 
201+                       < Separator  orientation = "vertical"  className = "!h-4"  /> 
202+                       < Button 
203+                         asChild 
204+                         size = "icon" 
205+                         variant = "ghost" 
206+                         className = "size-6 rounded-sm p-0" 
207+                         title = "Open in New Tab" 
208+                       > 
209+                         < Link  href = { `/blocks/${ item . name }  }  target = "_blank" > 
210+                           < span  className = "sr-only" > Open in New Tab</ span > 
211+                           < Fullscreen  /> 
212+                         </ Link > 
213+                       </ Button > 
214+                       < Separator  orientation = "vertical"  className = "!h-4"  /> 
215+                       < Button 
216+                         size = "icon" 
217+                         variant = "ghost" 
218+                         className = "size-6 rounded-sm p-0" 
219+                         onClick = { ( )  =>  { 
220+                           if  ( setIframeKey )  { 
221+                             setIframeKey ( ( k )  =>  k  +  1 ) 
222+                           } 
223+                         } } 
224+                         title = "Refresh Preview" 
225+                       > 
226+                         < RotateCw  /> 
227+                         < span  className = "sr-only" > Refresh Preview</ span > 
228+                       </ Button > 
229+                     </ ToggleGroup > 
230+                   </ div > 
231+                   < Separator  orientation = "vertical"  className = "mx-1 !h-4"  /> 
232+ 
233+                   < Button 
234+                     size = "sm" 
235+                     variant = "outline" 
236+                     className = "w-fit gap-1 px-2 shadow-none" 
237+                     onClick = { ( )  =>  { 
238+                       copyToClipboard ( `npx shadcn@latest add ${ item . name }  ) 
239+                     } } 
240+                   > 
241+                     { isCopied  ? < Check  />  : < Terminal  /> } 
242+                     < span > npx shadcn@latest add { item . name } </ span > 
243+                   </ Button > 
244+                   < Separator  orientation = "vertical"  className = "mx-1 !h-4"  /> 
245+                 </ React . Fragment > 
246+               ) 
247+             } 
248+ 
249+ 
250+           </ div > 
251+         ) 
252+       } 
253+ 
201254    </ div > 
202255  ) 
203256} 
204257
205258function  BlockViewerIframe ( )  { 
206259  const  {  iframeKey,  item }  =  useBlockViewer ( ) 
207260
208-   console . log ( "🚀 ~ BlockViewerIframe ~ item:" ,  item ) 
209- 
210- 
211261  const  Preview  =  React . useMemo ( ( )  =>  { 
212262
213-     if ( item . meta ?. isPro )  return  null 
263+     if   ( item . meta ?. isPro )  return  null 
214264
215265    const  Component  =  Index [ item . name ] ?. component ; 
216266
217-     if  ( ! Component   )  { 
267+     if  ( ! Component )  { 
218268      return  ( 
219269        < p  className = "text-sm text-muted-foreground" > 
220270          Component{ ' ' } 
@@ -484,6 +534,7 @@ function BlockViewer({
484534} )  { 
485535  return  ( 
486536    < BlockViewerProvider 
537+       blocks = { blocks } 
487538      highlightedFiles = { highlightedFiles } 
488539      item = { item } 
489540      tree = { tree } 
0 commit comments