11'use client'
22
33import { useCallback , useEffect , useRef , useState } from 'react'
4- import { type MotionValue , motion , useScroll , useTransform } from 'framer-motion'
4+ import { createLogger } from '@sim/logger'
5+ import { AnimatePresence , type MotionValue , motion , useScroll , useTransform } from 'framer-motion'
56import dynamic from 'next/dynamic'
6- import Link from 'next/link '
7+ import { useRouter } from 'next/navigation '
78import { Badge , ChevronDown } from '@/components/emcn'
89import { LandingWorkflowSeedStorage } from '@/lib/core/utils/browser-storage'
9-
1010import { cn } from '@/lib/core/utils/cn'
1111import { TEMPLATE_WORKFLOWS } from '@/app/(home)/components/templates/template-workflows'
12- import { createLogger } from '@sim/logger'
13- import { useRouter } from 'next/navigation'
12+
1413const logger = createLogger ( 'LandingTemplates' )
1514
1615const LandingPreviewWorkflow = dynamic (
@@ -338,7 +337,7 @@ function DotGrid({ className, cols, rows, gap = 0 }: DotGridProps) {
338337 } }
339338 >
340339 { Array . from ( { length : cols * rows } , ( _ , i ) => (
341- < div key = { i } className = 'h-[2px ] w-[2px ] rounded-full bg-[#2A2A2A]' />
340+ < div key = { i } className = 'h-[1.5px ] w-[1.5px ] rounded-full bg-[#2A2A2A]' />
342341 ) ) }
343342 </ div >
344343 )
@@ -425,8 +424,8 @@ export default function Templates() {
425424
426425 < div className = 'bg-[#1C1C1C]' >
427426 < DotGrid
428- className = 'border-[#2A2A2A] border-y bg-[#1C1C1C] p-[6px]'
429- cols = { 120 }
427+ className = 'overflow-hidden border-[#2A2A2A] border-y bg-[#1C1C1C] p-[6px]'
428+ cols = { 160 }
430429 rows = { 1 }
431430 gap = { 6 }
432431 />
@@ -450,7 +449,7 @@ export default function Templates() {
450449 </ svg >
451450 </ div >
452451
453- < div className = 'px-[80px] pt-[100px]' >
452+ < div className = 'px-[20px] pt-[60px] lg:px-[ 80px] lg: pt-[100px]' >
454453 < div className = 'flex flex-col items-start gap-[20px]' >
455454 < Badge
456455 variant = 'blue'
@@ -467,107 +466,132 @@ export default function Templates() {
467466
468467 < h2
469468 id = 'templates-heading'
470- className = 'font-[430] font-season text-[40px ] text-white leading-[100%] tracking-[-0.02em]'
469+ className = 'font-[430] font-season text-[28px ] text-white leading-[100%] tracking-[-0.02em] lg:text-[40px ]'
471470 >
472471 Ship your agent in minutes
473472 </ h2 >
474473
475- < p className = 'font-[430] font-season text-[#F6F6F0]/50 text-[16px ] leading-[125 %] tracking-[0.02em]' >
474+ < p className = 'font-[430] font-season text-[#F6F6F0]/50 text-[15px ] leading-[150 %] tracking-[0.02em] lg:text-[18px ]' >
476475 Pre-built templates for every use case—pick one, swap{ ' ' }
477476 < br className = 'hidden lg:inline' />
478477 models and tools to fit your stack, and deploy.
479478 </ p >
480479 </ div >
481480 </ div >
482481
483- < div className = 'mt-[73px] flex border-[#2A2A2A] border-y' >
484- < DotGrid
485- className = 'w-[80px] shrink-0 overflow-hidden border-[#2A2A2A] border-r p-[6px]'
486- cols = { 6 }
487- rows = { 55 }
488- gap = { 6 }
489- />
482+ < div className = 'mt-[40px] flex border-[#2A2A2A] border-y lg:mt-[73px]' >
483+ < div className = 'shrink-0' >
484+ < div className = 'h-full lg:hidden' >
485+ < DotGrid
486+ className = 'h-full w-[24px] overflow-hidden border-[#2A2A2A] border-r p-[4px]'
487+ cols = { 2 }
488+ rows = { 55 }
489+ gap = { 4 }
490+ />
491+ </ div >
492+ < div className = 'hidden h-full lg:block' >
493+ < DotGrid
494+ className = 'h-full w-[80px] overflow-hidden border-[#2A2A2A] border-r p-[6px]'
495+ cols = { 8 }
496+ rows = { 55 }
497+ gap = { 6 }
498+ />
499+ </ div >
500+ </ div >
490501
491- < div className = 'flex min-w-0 flex-1' >
502+ < div className = 'flex min-w-0 flex-1 flex-col lg:flex-row ' >
492503 < div
493504 role = 'tablist'
494505 aria-label = 'Workflow templates'
495- className = 'flex w-[300px] shrink-0 flex-col border-[#2A2A2A] border-r'
506+ className = 'flex w-full shrink-0 flex-col border-[#2A2A2A] lg:w-[300px] lg: border-r'
496507 >
497508 { TEMPLATE_WORKFLOWS . map ( ( workflow , index ) => {
498509 const isActive = index === activeIndex
499- const depth = DEPTH_CONFIGS [ workflow . id ]
500510 return (
501- < button
502- key = { workflow . id }
503- id = { `template-tab-${ index } ` }
504- type = 'button'
505- role = 'tab'
506- aria-selected = { isActive }
507- aria-controls = { TEMPLATES_PANEL_ID }
508- onClick = { ( ) => setActiveIndex ( index ) }
509- className = { cn (
510- 'relative text-left' ,
511- isActive
512- ? 'z-10'
513- : 'shadow-[inset_0_-1px_0_0_#2A2A2A] last:shadow-none hover:bg-[#232323]/50'
514- ) }
515- >
516- < div
517- className = 'pointer-events-none absolute top-[-8px] bottom-0 left-0 w-2'
518- style = { {
519- clipPath : LEFT_WALL_CLIP ,
520- backgroundColor : hexToRgba ( depth . color , 0.63 ) ,
521- opacity : isActive ? 1 : 0 ,
522- transition : isActive
523- ? 'opacity 250ms cubic-bezier(0.2, 0, 0, 1) 50ms'
524- : 'opacity 200ms cubic-bezier(0.4, 0, 1, 1)' ,
525- } }
526- />
527- < div
528- className = 'pointer-events-none absolute right-[-8px] bottom-0 left-2 h-2'
529- style = { {
530- ...buildBottomWallStyle ( depth ) ,
531- opacity : isActive ? 1 : 0 ,
532- transition : isActive
533- ? 'opacity 250ms cubic-bezier(0.2, 0, 0, 1) 50ms'
534- : 'opacity 200ms cubic-bezier(0.4, 0, 1, 1)' ,
535- } }
536- />
537- < div
538- className = 'relative flex items-center px-[12px] py-[10px]'
539- style = { {
540- transform : isActive ? 'translate(8px, -8px)' : 'translate(0px, 0px)' ,
541- backgroundColor : isActive ? '#242424' : 'transparent' ,
542- boxShadow : isActive
543- ? 'inset 0 0 0 1.5px #3E3E3E'
544- : 'inset 0 0 0 1.5px transparent' ,
545- transition : isActive
546- ? 'transform 350ms cubic-bezier(0.34, 1.4, 0.64, 1), background-color 250ms ease 30ms, box-shadow 250ms ease 30ms'
547- : 'transform 300ms cubic-bezier(0.4, 0, 0.2, 1), background-color 200ms ease, box-shadow 200ms ease' ,
548- } }
511+ < div key = { workflow . id } >
512+ < button
513+ id = { `template-tab-${ index } ` }
514+ type = 'button'
515+ role = 'tab'
516+ aria-selected = { isActive }
517+ aria-controls = { TEMPLATES_PANEL_ID }
518+ onClick = { ( ) => setActiveIndex ( index ) }
519+ className = { cn (
520+ 'relative w-full text-left' ,
521+ isActive
522+ ? 'z-10'
523+ : cn (
524+ 'flex items-center px-[12px] py-[10px] hover:bg-[#232323]/50' ,
525+ index < TEMPLATE_WORKFLOWS . length - 1 &&
526+ 'shadow-[inset_0_-1px_0_0_#2A2A2A]'
527+ )
528+ ) }
549529 >
550- < span
551- className = 'flex-1 font-[430] font-season text-[16px]'
552- style = { {
553- color : isActive ? '#FFFFFF' : 'rgba(246, 246, 240, 0.5)' ,
554- transition : 'color 250ms ease' ,
555- } }
556- >
557- { workflow . name }
558- </ span >
559- < ChevronDown
560- className = '-rotate-90 h-[11px] w-[11px] shrink-0'
561- style = { {
562- color : depth . color ,
563- opacity : isActive ? 1 : 0 ,
564- transition : isActive
565- ? 'opacity 200ms ease 150ms'
566- : 'opacity 150ms ease' ,
567- } }
568- />
569- </ div >
570- </ button >
530+ { isActive ? (
531+ ( ( ) => {
532+ const depth = DEPTH_CONFIGS [ workflow . id ]
533+ return (
534+ < >
535+ < div
536+ className = 'absolute top-[-8px] bottom-0 left-0 w-2'
537+ style = { {
538+ clipPath : LEFT_WALL_CLIP ,
539+ backgroundColor : hexToRgba ( depth . color , 0.63 ) ,
540+ } }
541+ />
542+ < div
543+ className = 'absolute right-[-8px] bottom-0 left-2 h-2'
544+ style = { buildBottomWallStyle ( depth ) }
545+ />
546+ < div className = '-translate-y-2 relative flex translate-x-2 items-center bg-[#242424] px-[12px] py-[10px] shadow-[inset_0_0_0_1.5px_#3E3E3E]' >
547+ < span className = 'flex-1 font-[430] font-season text-[16px] text-white' >
548+ { workflow . name }
549+ </ span >
550+ < ChevronDown
551+ className = '-rotate-90 h-[11px] w-[11px] shrink-0'
552+ style = { { color : depth . color } }
553+ />
554+ </ div >
555+ </ >
556+ )
557+ } ) ( )
558+ ) : (
559+ < span className = 'font-[430] font-season text-[#F6F6F0]/50 text-[16px]' >
560+ { workflow . name }
561+ </ span >
562+ ) }
563+ </ button >
564+
565+ < AnimatePresence >
566+ { isActive && isMobile && (
567+ < motion . div
568+ initial = { { height : 0 , opacity : 0 } }
569+ animate = { { height : 'auto' , opacity : 1 } }
570+ exit = { { height : 0 , opacity : 0 } }
571+ transition = { { duration : 0.25 , ease : [ 0.4 , 0 , 0.2 , 1 ] } }
572+ className = 'overflow-hidden'
573+ >
574+ < div className = 'aspect-[16/10] w-full border-[#2A2A2A] border-y bg-[#1b1b1b]' >
575+ < LandingPreviewWorkflow
576+ workflow = { workflow }
577+ animate
578+ fitViewOptions = { { padding : 0.15 , maxZoom : 1.3 } }
579+ />
580+ </ div >
581+ < div className = 'p-[12px]' >
582+ < button
583+ type = 'button'
584+ onClick = { handleUseTemplate }
585+ disabled = { isPreparingTemplate }
586+ className = 'inline-flex h-[32px] w-full cursor-pointer items-center justify-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] font-[430] font-season text-[14px] text-black transition-colors active:bg-[#E0E0E0]'
587+ >
588+ { isPreparingTemplate ? 'Preparing...' : 'Use template' }
589+ </ button >
590+ </ div >
591+ </ motion . div >
592+ ) }
593+ </ AnimatePresence >
594+ </ div >
571595 )
572596 } ) }
573597 </ div >
@@ -586,11 +610,13 @@ export default function Templates() {
586610 fitViewOptions = { { padding : 0.15 , maxZoom : 1.3 } }
587611 />
588612 </ div >
589- < Link
590- href = '/signup'
591- className = 'group/cta absolute top-[16px] right-[16px] z-10 inline-flex h-[32px] items-center gap-[6px] rounded-[5px] border border-[#33C482] bg-[#33C482] px-[10px] font-[430] font-season text-[14px] text-black transition-[filter] hover:brightness-110'
613+ < button
614+ type = 'button'
615+ onClick = { handleUseTemplate }
616+ disabled = { isPreparingTemplate }
617+ className = 'group/cta absolute top-[16px] right-[16px] z-10 inline-flex h-[32px] cursor-pointer items-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
592618 >
593- Use template
619+ { isPreparingTemplate ? 'Preparing...' : ' Use template' }
594620 < span className = 'relative h-[10px] w-[10px] shrink-0' >
595621 < ChevronDown className = '-rotate-90 absolute inset-0 h-[10px] w-[10px] transition-opacity duration-150 group-hover/cta:opacity-0' />
596622 < svg
@@ -609,19 +635,31 @@ export default function Templates() {
609635 />
610636 </ svg >
611637 </ span >
612- </ Link >
638+ </ button >
613639 </ div >
614640 </ div >
615641
616- < DotGrid
617- className = 'w-[80px] shrink-0 overflow-hidden border-[#2A2A2A] border-l p-[6px]'
618- cols = { 6 }
619- rows = { 55 }
620- gap = { 6 }
621- />
642+ < div className = 'shrink-0' >
643+ < div className = 'h-full lg:hidden' >
644+ < DotGrid
645+ className = 'h-full w-[24px] overflow-hidden border-[#2A2A2A] border-l p-[4px]'
646+ cols = { 2 }
647+ rows = { 55 }
648+ gap = { 4 }
649+ />
650+ </ div >
651+ < div className = 'hidden h-full lg:block' >
652+ < DotGrid
653+ className = 'h-full w-[80px] overflow-hidden border-[#2A2A2A] border-l p-[6px]'
654+ cols = { 8 }
655+ rows = { 55 }
656+ gap = { 6 }
657+ />
658+ </ div >
659+ </ div >
622660 </ div >
623661 </ div >
624662 </ div >
625663 </ section >
626664 )
627- }
665+ }
0 commit comments