Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"next": "16.1.6",
"next-themes": "^0.4.6",
"postgres": "^3.4.5",
"react": "19.2.1",
"react-dom": "19.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"shiki": "4.0.0",
"tailwind-merge": "^3.0.2"
},
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export type { Category, ModuleTag, Tag, TemplatePrompt } from './consts'
export { CATEGORY_META, MODULE_META, TEMPLATES } from './consts'
export { TemplatePrompts } from './template-prompts'
Original file line number Diff line number Diff line change
@@ -1,96 +1,151 @@
import type { ComponentType, SVGProps } from 'react'
'use client'

import { useState } from 'react'
import Image from 'next/image'
import { Search, Table } from '@/components/emcn/icons'
import { GmailIcon, GoogleCalendarIcon } from '@/components/icons'
import { MarkdownIcon } from '@/components/icons/document-icons'

interface TemplatePrompt {
icon: ComponentType<SVGProps<SVGSVGElement>>
title: string
prompt: string
image: string
import { ChevronDown } from '@/components/emcn/icons'
import type { Category, ModuleTag } from './consts'
import { CATEGORY_META, MODULE_META, TEMPLATES } from './consts'

const FEATURED_TEMPLATES = TEMPLATES.filter((t) => t.featured)
const EXTRA_TEMPLATES = TEMPLATES.filter((t) => !t.featured)

/** Group non-featured templates by category, preserving category order. */
function getGroupedExtras() {
const groups: { category: Category; label: string; templates: typeof TEMPLATES }[] = []
const byCategory = new Map<Category, typeof TEMPLATES>()

for (const t of EXTRA_TEMPLATES) {
const existing = byCategory.get(t.category)
if (existing) {
existing.push(t)
} else {
const arr = [t]
byCategory.set(t.category, arr)
}
}

for (const [key, meta] of Object.entries(CATEGORY_META)) {
const cat = key as Category
if (cat === 'popular') continue
const items = byCategory.get(cat)
if (items?.length) {
groups.push({ category: cat, label: meta.label, templates: items })
}
}

return groups
}

const TEMPLATES: TemplatePrompt[] = [
{
icon: Table,
title: 'Self-populating CRM',
prompt:
'Create a self-healing CRM table that keeps track of all my customers by integrating with my existing data sources. Schedule a recurring job every morning to automatically pull updates from all relevant data sources and keep my CRM up to date.',
image: '/templates/crm-light.png',
},
{
icon: GoogleCalendarIcon,
title: 'Meeting prep agent',
prompt:
'Create an agent that checks my calendar each morning, pulls context on every attendee and topic, and prepares a brief for each meeting so I walk in fully prepared.',
image: '/templates/meeting-prep-dark.png',
},
{
icon: MarkdownIcon,
title: 'Resolve todo list',
prompt:
'Create a file of all my todos then go one by one and check off every time a todo is done. Look at my calendar and see what I have to do.',
image: '/templates/todo-list-light.png',
},
{
icon: Search,
title: 'Research assistant',
prompt:
'Build an agent that takes a topic, searches the web for the latest information, summarizes key findings, and compiles them into a clean document I can review.',
image: '/templates/research-assistant-dark.png',
},
{
icon: GmailIcon,
title: 'Auto-reply agent',
prompt: 'Create a Gmail agent that drafts responses to relevant emails automatically.',
image: '/templates/gmail-agent-dark.png',
},
{
icon: Table,
title: 'Expense tracker',
prompt:
'Create a table that tracks all my expenses by pulling transactions from my connected accounts. Categorize each expense automatically and generate a weekly summary report.',
image: '/templates/expense-tracker-light.png',
},
]
const GROUPED_EXTRAS = getGroupedExtras()

function ModulePills({ modules }: { modules: ModuleTag[] }) {
return (
<div className='flex flex-wrap gap-[4px]'>
{modules.map((mod) => (
<span
key={mod}
className='rounded-full bg-[var(--surface-3)] px-[6px] py-[1px] text-[11px] text-[var(--text-secondary)]'
>
{MODULE_META[mod].label}
</span>
))}
</div>
)
}

interface TemplatePromptsProps {
onSelect: (prompt: string) => void
}

export function TemplatePrompts({ onSelect }: TemplatePromptsProps) {
const [expanded, setExpanded] = useState(false)

return (
<div className='grid grid-cols-3 gap-[16px]'>
{TEMPLATES.map((template) => {
const Icon = template.icon
return (
<button
key={template.title}
type='button'
onClick={() => onSelect(template.prompt)}
className='group flex cursor-pointer flex-col text-left'
>
<div className='overflow-hidden rounded-[10px] border border-[var(--border-1)]'>
<div className='relative h-[120px] w-full overflow-hidden'>
<Image
src={template.image}
alt={template.title}
fill
unoptimized
className='object-cover transition-transform duration-300 group-hover:scale-105'
/>
</div>
<div className='flex items-center gap-[6px] border-[var(--border-1)] border-t bg-[var(--white)] px-[10px] py-[6px] dark:bg-[var(--surface-4)]'>
<Icon className='h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
<span className='font-base text-[14px] text-[var(--text-body)]'>
{template.title}
</span>
<div className='flex flex-col gap-[24px]'>
{/* Featured grid */}
<div className='grid grid-cols-3 gap-[16px]'>
{FEATURED_TEMPLATES.map((template) => (
<TemplateCard key={template.title} template={template} onSelect={onSelect} />
))}
</div>

{/* Expand / collapse */}
<button
type='button'
onClick={() => setExpanded((prev) => !prev)}
aria-expanded={expanded}
className='flex items-center justify-center gap-[6px] text-[13px] text-[var(--text-secondary)] transition-colors hover:text-[var(--text-body)]'
>
{expanded ? (
<>
Show less <ChevronDown className='h-[14px] w-[14px] rotate-180' />
</>
) : (
<>
More examples <ChevronDown className='h-[14px] w-[14px]' />
</>
)}
</button>

{/* Categorized extras */}
{expanded && (
<div className='flex flex-col gap-[32px]'>
{GROUPED_EXTRAS.map((group) => (
<div key={group.category} className='flex flex-col gap-[12px]'>
<h3 className='font-medium text-[13px] text-[var(--text-secondary)]'>
{group.label}
</h3>
<div className='grid grid-cols-3 gap-[16px]'>
{group.templates.map((template) => (
<TemplateCard key={template.title} template={template} onSelect={onSelect} />
))}
</div>
</div>
</button>
)
})}
))}
</div>
)}
</div>
)
}

interface TemplateCardProps {
template: (typeof TEMPLATES)[number]
onSelect: (prompt: string) => void
}

function TemplateCard({ template, onSelect }: TemplateCardProps) {
const Icon = template.icon

return (
<button
type='button'
onClick={() => onSelect(template.prompt)}
className='group flex cursor-pointer flex-col text-left'
>
<div className='overflow-hidden rounded-[10px] border border-[var(--border-1)]'>
<div className='relative h-[120px] w-full overflow-hidden'>
{template.image ? (
<Image
src={template.image}
alt={template.title}
fill
unoptimized
className='object-cover transition-transform duration-300 group-hover:scale-105'
/>
) : (
<div className='flex h-full w-full items-center justify-center bg-[var(--surface-3)] transition-colors group-hover:bg-[var(--surface-4)]'>
<Icon className='h-[32px] w-[32px] text-[var(--text-icon)] opacity-40' />
</div>
)}
</div>
<div className='flex flex-col gap-[4px] border-[var(--border-1)] border-t bg-[var(--white)] px-[10px] py-[6px] dark:bg-[var(--surface-4)]'>
<div className='flex items-center gap-[6px]'>
<Icon className='h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
<span className='font-base text-[14px] text-[var(--text-body)]'>{template.title}</span>
</div>
<ModulePills modules={template.modules} />
</div>
</div>
</button>
)
}
4 changes: 2 additions & 2 deletions apps/sim/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@
"posthog-js": "1.334.1",
"posthog-node": "5.9.2",
"prismjs": "^1.30.0",
"react": "19.2.1",
"react-dom": "19.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-hook-form": "^7.54.2",
"react-markdown": "^10.1.0",
"react-simple-code-editor": "^0.14.1",
Expand Down
17 changes: 9 additions & 8 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"release": "bun run scripts/create-single-release.ts"
},
"overrides": {
"react": "19.2.1",
"react-dom": "19.2.1",
"react": "19.2.4",
"react-dom": "19.2.4",
"next": "16.1.6",
"@next/env": "16.1.6",
"drizzle-orm": "^0.44.5",
Expand Down
Loading