-
+
+
-
- Grab your team. Build agents together in real-time inside your workspace.
+
+ Grab your team. Build agents together
+ in real-time inside your workspace.
-
-
+
+
@@ -319,10 +301,29 @@ export default function Collaboration() {
+
+
+
+
+
+
+
+ Blog
+
+
+ How we built realtime collaboration
+
+
+
+
+
+
+
+ {!isWorkspaceTab && (
+
+
+
+ )}
+
+
+ )
+}
+
+// ─── Mothership Preview ───────────────────────────────────────────
+
+const TYPING_PROMPT = 'Clear all my todos this week'
+const TYPE_SPEED = 45
+const TYPE_START_DELAY = 500
+const PAUSE_AFTER_TYPE = 800
+const CARD_SIZE = 100
+const CARD_GAP = 8
+const GRID_STEP = CARD_SIZE + CARD_GAP
+const GRID_PAD = 8
+
+type CardVariant = 'prompt' | 'table' | 'workflow' | 'knowledge' | 'logs' | 'file'
+
+interface CardDef {
+ row: number
+ col: number
+ variant: CardVariant
+ label: string
+ color?: string
+}
+
+const MOTHERSHIP_CARDS: CardDef[] = [
+ { row: 0, col: 0, variant: 'prompt', label: 'prompt.md' },
+ { row: 1, col: 0, variant: 'table', label: 'Leads' },
+ { row: 0, col: 1, variant: 'workflow', label: 'Email Bot', color: '#7C3AED' },
+ { row: 1, col: 1, variant: 'knowledge', label: 'Company KB' },
+ { row: 2, col: 0, variant: 'logs', label: 'Run Logs' },
+ { row: 0, col: 2, variant: 'file', label: 'notes.md' },
+ { row: 2, col: 1, variant: 'workflow', label: 'Onboarding', color: '#2563EB' },
+ { row: 1, col: 2, variant: 'table', label: 'Contacts' },
+ { row: 2, col: 2, variant: 'file', label: 'report.pdf' },
+ { row: 3, col: 0, variant: 'table', label: 'Tickets' },
+ { row: 0, col: 3, variant: 'knowledge', label: 'Product Wiki' },
+ { row: 3, col: 1, variant: 'logs', label: 'Audit Trail' },
+ { row: 1, col: 3, variant: 'workflow', label: 'Support', color: '#059669' },
+ { row: 2, col: 3, variant: 'file', label: 'data.csv' },
+ { row: 3, col: 2, variant: 'table', label: 'Users' },
+ { row: 3, col: 3, variant: 'knowledge', label: 'HR Docs' },
+ { row: 0, col: 4, variant: 'workflow', label: 'Pipeline', color: '#DC2626' },
+ { row: 1, col: 4, variant: 'logs', label: 'API Logs' },
+ { row: 2, col: 4, variant: 'table', label: 'Orders' },
+ { row: 3, col: 4, variant: 'file', label: 'config.json' },
+ { row: 0, col: 5, variant: 'logs', label: 'Deploys' },
+ { row: 1, col: 5, variant: 'table', label: 'Campaigns' },
+ { row: 2, col: 5, variant: 'workflow', label: 'Intake', color: '#D97706' },
+ { row: 3, col: 5, variant: 'knowledge', label: 'Research' },
+ { row: 4, col: 0, variant: 'file', label: 'readme.md' },
+ { row: 4, col: 1, variant: 'table', label: 'Revenue' },
+ { row: 4, col: 2, variant: 'workflow', label: 'Sync', color: '#0891B2' },
+ { row: 4, col: 3, variant: 'logs', label: 'Errors' },
+ { row: 4, col: 4, variant: 'table', label: 'Inventory' },
+ { row: 4, col: 5, variant: 'file', label: 'schema.json' },
+ { row: 0, col: 6, variant: 'table', label: 'Analytics' },
+ { row: 1, col: 6, variant: 'workflow', label: 'Digest', color: '#6366F1' },
+ { row: 0, col: 7, variant: 'file', label: 'brief.md' },
+ { row: 2, col: 6, variant: 'knowledge', label: 'Playbooks' },
+ { row: 1, col: 7, variant: 'logs', label: 'Webhooks' },
+ { row: 3, col: 6, variant: 'file', label: 'export.csv' },
+ { row: 2, col: 7, variant: 'workflow', label: 'Alerts', color: '#E11D48' },
+ { row: 4, col: 6, variant: 'logs', label: 'Metrics' },
+ { row: 3, col: 7, variant: 'table', label: 'Feedback' },
+ { row: 4, col: 7, variant: 'knowledge', label: 'Runbooks' },
+]
+
+const EXPAND_TARGETS: Record
= {
+ 1: { row: 1, col: 0 },
+ 2: { row: 0, col: 2 },
+ 3: { row: 1, col: 1 },
+ 4: { row: 2, col: 0 },
+}
+
+const EXPAND_ROW_COUNTS: Record = {
+ 1: 10,
+ 2: 10,
+ 3: 10,
+ 4: 7,
+}
+
+function WorkspacePreview({ activeTab, isActive }: { activeTab: number; isActive: boolean }) {
+ const containerRef = useRef(null)
+ const inView = useInView(containerRef, { once: true, margin: '-80px' })
+
+ const [typedText, setTypedText] = useState('')
+ const [showGrid, setShowGrid] = useState(false)
+ const hasPlayedTyping = useRef(false)
+ const gridAnimateIn = useRef(true)
+
+ const [expandedTab, setExpandedTab] = useState(null)
+ const [revealedRows, setRevealedRows] = useState(0)
+
+ const isMothership = activeTab === 0 && isActive
+ const isExpandTab = activeTab >= 1 && activeTab <= 4 && isActive
+ const expandTarget = EXPAND_TARGETS[activeTab] ?? null
+
+ useEffect(() => {
+ if (!inView || showGrid || !isActive || activeTab === 0) return
+ gridAnimateIn.current = false
+ setShowGrid(true)
+ }, [inView, isActive, activeTab, showGrid])
+
+ useEffect(() => {
+ if (!inView || !isMothership || hasPlayedTyping.current) return
+ hasPlayedTyping.current = true
+
+ const timers: ReturnType[] = []
+ let typeTimer: ReturnType | undefined
+
+ timers.push(
+ setTimeout(() => {
+ let i = 0
+ typeTimer = setInterval(() => {
+ i++
+ setTypedText(TYPING_PROMPT.slice(0, i))
+ if (i >= TYPING_PROMPT.length) {
+ clearInterval(typeTimer)
+ typeTimer = undefined
+ timers.push(
+ setTimeout(() => {
+ gridAnimateIn.current = true
+ setShowGrid(true)
+ }, PAUSE_AFTER_TYPE)
+ )
+ }
+ }, TYPE_SPEED)
+ }, TYPE_START_DELAY)
+ )
+
+ return () => {
+ timers.forEach(clearTimeout)
+ if (typeTimer) clearInterval(typeTimer)
+ }
+ }, [inView, isMothership])
+
+ useEffect(() => {
+ if (!isExpandTab || !showGrid) {
+ if (!isExpandTab) {
+ setExpandedTab(null)
+ setRevealedRows(0)
+ }
+ return
+ }
+ setExpandedTab(null)
+ setRevealedRows(0)
+ const timer = setTimeout(() => setExpandedTab(activeTab), 300)
+ return () => clearTimeout(timer)
+ }, [isExpandTab, activeTab, showGrid])
+
+ useEffect(() => {
+ const maxRows = expandedTab !== null ? (EXPAND_ROW_COUNTS[expandedTab] ?? 0) : 0
+ if (expandedTab === null || revealedRows >= maxRows) return
+ const delay = revealedRows === 0 ? 800 : 150
+ const timer = setTimeout(() => setRevealedRows((prev) => prev + 1), delay)
+ return () => clearTimeout(timer)
+ }, [expandedTab, revealedRows])
+
+ const isExpanded = expandedTab !== null
+
+ return (
+
+
+
+
+ {isMothership && !showGrid && inView && (
+
+
+
+ )}
+
+
+ {showGrid && (
+
+ {MOTHERSHIP_CARDS.map((card) => (
+
+
+
+ ))}
+
+ )}
+
+ {isExpanded && expandTarget && (
+
+ {expandedTab === 1 && }
+ {expandedTab === 2 && }
+ {expandedTab === 3 && }
+ {expandedTab === 4 && }
+
+ )}
+
+ )
+}
+
+// ─── Mock User Input ──────────────────────────────────────────────
+
+function MockUserInput({ text }: { text: string }) {
+ return (
+
+ )
+}
+
+// ─── Mini Card Components ─────────────────────────────────────────
+
+function MiniCard({
+ variant,
+ label,
+ color,
+}: {
+ variant: CardVariant
+ label: string
+ color?: string
+}) {
+ return (
+
+ )
+}
+
+function MiniCardHeader({
+ variant,
+ label,
+ color,
+}: {
+ variant: CardVariant
+ label: string
+ color?: string
+}) {
+ return (
+
+
+ {label}
+
+ )
+}
+
+function MiniCardIcon({ variant, color }: { variant: CardVariant; color?: string }) {
+ const cls = 'h-[7px] w-[7px] flex-shrink-0 text-[#BBB]'
+
+ switch (variant) {
+ case 'prompt':
+ case 'file':
+ return
+ case 'table':
+ return
+ case 'workflow': {
+ const c = color ?? '#7C3AED'
+ return (
+
+ )
+ }
+ case 'knowledge':
+ return
+ case 'logs':
+ return
+ }
+}
+
+function MiniCardBody({ variant, color }: { variant: CardVariant; color?: string }) {
+ switch (variant) {
+ case 'prompt':
+ return
+ case 'file':
+ return
+ case 'table':
+ return
+ case 'workflow':
+ return
+ case 'knowledge':
+ return
+ case 'logs':
+ return
+ }
+}
+
+function PromptCardBody() {
+ return (
+
+ )
+}
+
+function FileCardBody() {
+ return (
+
+ )
+}
+
+const TABLE_ROW_WIDTHS = [
+ [22, 18, 14],
+ [16, 20, 10],
+ [24, 12, 16],
+ [18, 16, 12],
+ [20, 22, 18],
+ [14, 18, 8],
+] as const
+
+function TableCardBody() {
+ return (
+
+
+ {TABLE_ROW_WIDTHS.map((row, i) => (
+
+ ))}
+
+ )
+}
+
+function WorkflowCardBody({ color }: { color: string }) {
+ return (
+
+ )
+}
+
+const KB_WIDTHS = [70, 85, 55, 80, 48] as const
+
+function KnowledgeCardBody() {
+ return (
+
+ {KB_WIDTHS.map((w, i) => (
+
+ ))}
+
+ )
+}
+
+const LOG_ENTRIES = [
+ { color: '#22C55E', width: 65 },
+ { color: '#22C55E', width: 78 },
+ { color: '#EAB308', width: 52 },
+ { color: '#22C55E', width: 70 },
+ { color: '#EF4444', width: 58 },
+ { color: '#22C55E', width: 74 },
+] as const
+
+function LogsCardBody() {
+ return (
+
+ {LOG_ENTRIES.map((entry, i) => (
+
+ ))}
+
+ )
+}
+
+// ─── Tables Mock Data ─────────────────────────────────────────────
+
+const MOCK_TABLE_COLUMNS = ['Name', 'Email', 'Company', 'Status'] as const
+
+const MOCK_TABLE_DATA = [
+ ['Sarah Chen', 'sarah@acme.co', 'Acme Inc', 'Qualified'],
+ ['James Park', 'james@globex.io', 'Globex', 'New'],
+ ['Maria Santos', 'maria@initech.com', 'Initech', 'Contacted'],
+ ['Alex Kim', 'alex@umbrella.co', 'Umbrella Corp', 'Qualified'],
+ ['Emma Wilson', 'emma@stark.io', 'Stark Industries', 'New'],
+ ['David Lee', 'david@waystar.com', 'Waystar', 'Contacted'],
+ ['Priya Patel', 'priya@hooli.io', 'Hooli', 'New'],
+ ['Tom Zhang', 'tom@weyland.co', 'Weyland Corp', 'Qualified'],
+ ['Nina Kowalski', 'nina@oscorp.io', 'Oscorp', 'Contacted'],
+ ['Ryan Murphy', 'ryan@massiveD.co', 'Massive Dynamic', 'New'],
+] as const
+
+const MOCK_MD_SOURCE = `# Meeting Notes
+
+## Action Items
+
+- Review Q1 metrics with Sarah
+- Update API documentation
+- Schedule design review for v2.0
+
+## Discussion Points
+
+The team agreed to prioritize the new onboarding flow. Key decisions:
+
+1. Migrate to the new auth provider by end of March
+2. Ship the dashboard redesign in two phases
+3. Add automated testing for all critical paths
+
+## Next Steps
+
+Follow up with engineering on the timeline for the API v2 migration. Draft the proposal for the board meeting next week.`
+
+const MOCK_KB_COLUMNS = ['Name', 'Size', 'Tokens', 'Chunks', 'Status'] as const
+
+const KB_FILE_ICONS: Record>> = {
+ pdf: PdfIcon,
+ md: MarkdownIcon,
+ csv: CsvIcon,
+ json: JsonIcon,
+}
+
+function getKBFileIcon(filename: string) {
+ const ext = filename.split('.').pop()?.toLowerCase() ?? ''
+ return KB_FILE_ICONS[ext] ?? File
+}
+
+const MOCK_KB_DATA = [
+ ['product-specs.pdf', '4.2 MB', '12.4k', '86', 'enabled'],
+ ['eng-handbook.md', '1.8 MB', '8.2k', '54', 'enabled'],
+ ['api-reference.json', '920 KB', '4.1k', '32', 'enabled'],
+ ['release-notes.md', '340 KB', '2.8k', '18', 'enabled'],
+ ['onboarding-guide.pdf', '2.1 MB', '6.5k', '42', 'processing'],
+ ['data-export.csv', '560 KB', '3.4k', '24', 'enabled'],
+ ['runbook.md', '280 KB', '1.9k', '14', 'enabled'],
+ ['compliance.pdf', '180 KB', '1.2k', '8', 'disabled'],
+ ['style-guide.md', '410 KB', '2.6k', '20', 'enabled'],
+ ['metrics.csv', '1.4 MB', '5.8k', '38', 'enabled'],
+] as const
+
+function MockFullFiles() {
+ return (
+
+
+
+
+ Files
+ /
+ meeting-notes.md
+
+
+
+
+
+
+ {MOCK_MD_SOURCE}
+
+
+
+
+
+
+
+
+ Meeting Notes
+
+
+ Action Items
+
+
+
+ Review Q1 metrics with Sarah
+
+
+ Update API documentation
+
+
+ Schedule design review for v2.0
+
+
+
+ Discussion Points
+
+
+ The team agreed to prioritize the new onboarding flow. Key decisions:
+
+
+
+ Migrate to the new auth provider by end of March
+
+
+ Ship the dashboard redesign in two phases
+
+
+ Add automated testing for all critical paths
+
+
+
+ Next Steps
+
+
+ Follow up with engineering on the timeline for the API v2 migration. Draft the
+ proposal for the board meeting next week.
+
+
+
+
+
+ )
+}
+
+const KB_STATUS_STYLES: Record = {
+ enabled: { bg: '#DCFCE7', text: '#166534', label: 'Enabled' },
+ disabled: { bg: '#F3F4F6', text: '#6B7280', label: 'Disabled' },
+ processing: { bg: '#F3E8FF', text: '#7C3AED', label: 'Processing' },
+}
+
+function MockFullKnowledgeBase({ revealedRows }: { revealedRows: number }) {
+ return (
+
+
+
+
+ Knowledge Base
+ /
+ Company KB
+
+
+
+
+
+
+ Sort
+
+
+ Filter
+
+
+
+
+
+
+
+
+ {MOCK_KB_COLUMNS.map((col) => (
+
+ ))}
+
+
+
+
+
+
+ {MOCK_KB_COLUMNS.map((col) => (
+
+ {col}
+
+ ))}
+
+
+
+ {MOCK_KB_DATA.slice(0, revealedRows).map((row, i) => {
+ const status = KB_STATUS_STYLES[row[4]] ?? KB_STATUS_STYLES.enabled
+ const DocIcon = getKBFileIcon(row[0])
+ return (
+
+
+ {i + 1}
+
+
+
+
+ {row[0]}
+
+
+ {row.slice(1, 4).map((cell, j) => (
+
+ {cell}
+
+ ))}
+
+
+ {status.label}
+
+
+
+ )
+ })}
+
+
+
+
+ )
+}
+
+const MOCK_LOG_COLORS = [
+ '#7C3AED',
+ '#2563EB',
+ '#059669',
+ '#DC2626',
+ '#D97706',
+ '#7C3AED',
+ '#0891B2',
+]
+
+const MOCK_LOG_DATA = [
+ ['Email Bot', 'Mar 17, 2:14 PM', 'success', '$0.003', 'API', '1.2s'],
+ ['Lead Scorer', 'Mar 17, 2:10 PM', 'success', '$0.008', 'Schedule', '3.4s'],
+ ['Support Bot', 'Mar 17, 1:55 PM', 'error', '$0.002', 'Webhook', '0.8s'],
+ ['Onboarding', 'Mar 17, 1:42 PM', 'success', '$0.005', 'Manual', '2.1s'],
+ ['Pipeline', 'Mar 17, 1:30 PM', 'success', '$0.012', 'API', '4.6s'],
+ ['Email Bot', 'Mar 17, 1:15 PM', 'success', '$0.003', 'Schedule', '1.1s'],
+ ['Intake', 'Mar 17, 12:58 PM', 'success', '$0.006', 'Webhook', '2.8s'],
+] as const
+
+const LOG_STATUS_STYLES: Record = {
+ success: { bg: '#DCFCE7', text: '#166534', label: 'Success' },
+ error: { bg: '#FEE2E2', text: '#991B1B', label: 'Error' },
+}
+
+function MockFullLogs({ revealedRows }: { revealedRows: number }) {
+ const [showSidebar, setShowSidebar] = useState(false)
+
+ useEffect(() => {
+ if (revealedRows < MOCK_LOG_DATA.length) return
+ const timer = setTimeout(() => setShowSidebar(true), 400)
+ return () => clearTimeout(timer)
+ }, [revealedRows])
+
+ const selectedRow = 0
+
+ return (
+
+
+
+
+
+
+
+ {['Workflow', 'Date', 'Status', 'Cost', 'Trigger', 'Duration'].map((col) => (
+
+ ))}
+
+
+
+ {['Workflow', 'Date', 'Status', 'Cost', 'Trigger', 'Duration'].map((col) => (
+
+ {col}
+
+ ))}
+
+
+
+ {MOCK_LOG_DATA.slice(0, revealedRows).map((row, i) => {
+ const statusStyle = LOG_STATUS_STYLES[row[2]] ?? LOG_STATUS_STYLES.success
+ const isSelected = showSidebar && i === selectedRow
+ return (
+
+
+
+
+ {row[0]}
+
+
+
+ {row[1]}
+
+
+
+ {statusStyle.label}
+
+
+
+ {row[3]}
+
+
+
+ {row[4]}
+
+
+
+ {row[5]}
+
+
+ )
+ })}
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function MockLogDetailsSidebar() {
+ return (
+
+
+
+
+
+
+
Timestamp
+
+ Mar 17
+ 2:14 PM
+
+
+
+
+
+
+
+ Level
+
+ Success
+
+
+
+ Trigger
+
+ API
+
+
+
+ Duration
+ 1.2s
+
+
+
+
+
Workflow Output
+
+ {'{\n "result": "processed",\n "emails": 3,\n "status": "complete"\n}'}
+
+
+
+
+ )
+}
+
+function MockFullTable({ revealedRows }: { revealedRows: number }) {
+ return (
+
+
+
+
+
+
+ Sort
+
+
+ Filter
+
+
+
+
+
+
+
+
+ {MOCK_TABLE_COLUMNS.map((col) => (
+
+ ))}
+
+
+
+
+
+
+ {MOCK_TABLE_COLUMNS.map((col) => (
+
+
+
+ {col}
+
+
+
+ ))}
+
+
+
+ {MOCK_TABLE_DATA.slice(0, revealedRows).map((row, i) => (
+
+
+ {i + 1}
+
+ {row.map((cell, j) => (
+
+ {cell}
+
+ ))}
+
+ ))}
+
+
+
+
+ )
+}
+
+function ColumnTypeIcon() {
+ return (
+
+
+
+ )
+}
+
+// ─── Default Preview (scattered icons for other tabs) ─────────────
interface IconEntry {
key: string
@@ -31,25 +1095,53 @@ const SCATTERED_ICONS: IconEntry[] = [
{ key: 'anthropic', icon: AnthropicIcon, label: 'Anthropic', top: '10%', left: '78%' },
{ key: 'gmail', icon: GmailIcon, label: 'Gmail', top: '24%', left: '90%' },
{ key: 'salesforce', icon: SalesforceIcon, label: 'Salesforce', top: '28%', left: '6%' },
- { key: 'table', icon: Table, label: 'Tables', top: '22%', left: '30%' },
+ {
+ key: 'table',
+ icon: Table,
+ label: 'Tables',
+ top: '22%',
+ left: '30%',
+ color: 'var(--text-icon)',
+ },
{ key: 'xai', icon: xAIIcon, label: 'xAI', top: '26%', left: '66%' },
- { key: 'hubspot', icon: HubspotIcon, label: 'HubSpot', top: '55%', left: '4%', color: '#FF7A59' },
- { key: 'database', icon: Database, label: 'Database', top: '74%', left: '68%' },
- { key: 'file', icon: File, label: 'Files', top: '70%', left: '18%' },
+ {
+ key: 'hubspot',
+ icon: HubspotIcon,
+ label: 'HubSpot',
+ top: '55%',
+ left: '4%',
+ color: '#FF7A59',
+ },
+ {
+ key: 'database',
+ icon: Database,
+ label: 'Database',
+ top: '74%',
+ left: '68%',
+ color: 'var(--text-icon)',
+ },
+ { key: 'file', icon: File, label: 'Files', top: '70%', left: '18%', color: 'var(--text-icon)' },
{ key: 'gemini', icon: GeminiIcon, label: 'Gemini', top: '58%', left: '86%' },
- { key: 'logs', icon: Library, label: 'Logs', top: '86%', left: '44%' },
+ {
+ key: 'logs',
+ icon: Library,
+ label: 'Logs',
+ top: '86%',
+ left: '44%',
+ color: 'var(--text-icon)',
+ },
{ key: 'groq', icon: GroqIcon, label: 'Groq', top: '90%', left: '82%' },
]
const EXPLODE_STAGGER = 0.04
const EXPLODE_BASE_DELAY = 0.1
-export function FeaturesPreview() {
+function DefaultPreview() {
const containerRef = useRef(null)
const inView = useInView(containerRef, { once: true, margin: '-80px' })
return (
-
+
-
-
+
+
{HEADING_LETTERS.map((char, i) => (
@@ -225,18 +230,25 @@ export default function Features() {
-
+
-
-
+
+
{FEATURE_TABS.map((tab, index) => (
@@ -246,10 +258,17 @@ export default function Features() {
role='tab'
aria-selected={index === activeTab}
onClick={() => setActiveTab(index)}
- className='relative flex h-full flex-1 items-center justify-center border-[#E9E9E9] border-l font-medium font-season text-[#212121] text-[14px] uppercase'
+ className={`relative h-full flex-1 items-center justify-center whitespace-nowrap px-[12px] font-medium font-season text-[#212121] text-[12px] uppercase lg:px-0 lg:text-[14px]${tab.hideOnMobile ? ' hidden lg:flex' : ' flex'}${index > 0 ? ' border-[#E9E9E9] border-l' : ''}`}
style={{ backgroundColor: index === activeTab ? '#FDFDFD' : '#F6F6F6' }}
>
- {tab.label}
+ {tab.mobileLabel ? (
+ <>
+
{tab.mobileLabel}
+
{tab.label}
+ >
+ ) : (
+ tab.label
+ )}
{index === activeTab && (
{tab.segments.map(([opacity, width], i) => (
@@ -269,16 +288,23 @@ export default function Features() {
))}
-
+
-
-
+
+
-
+
{FEATURE_TABS[activeTab].title}
-
+
{FEATURE_TABS[activeTab].description}
@@ -307,10 +333,10 @@ export default function Features() {
-
+
-
+
diff --git a/apps/sim/app/(home)/components/footer/footer-cta.tsx b/apps/sim/app/(home)/components/footer/footer-cta.tsx
new file mode 100644
index 00000000000..c01276e2b2b
--- /dev/null
+++ b/apps/sim/app/(home)/components/footer/footer-cta.tsx
@@ -0,0 +1,100 @@
+'use client'
+
+import { useCallback, useRef, useState } from 'react'
+import { ArrowUp } from 'lucide-react'
+import Link from 'next/link'
+import { useLandingSubmit } from '@/app/(home)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
+import { useAnimatedPlaceholder } from '@/app/workspace/[workspaceId]/home/hooks/use-animated-placeholder'
+
+const MAX_HEIGHT = 120
+
+const CTA_BUTTON =
+ 'inline-flex items-center h-[32px] rounded-[5px] border px-[10px] font-[430] font-season text-[14px]'
+
+export function FooterCTA() {
+ const landingSubmit = useLandingSubmit()
+ const [inputValue, setInputValue] = useState('')
+ const textareaRef = useRef
(null)
+ const animatedPlaceholder = useAnimatedPlaceholder()
+
+ const isEmpty = inputValue.trim().length === 0
+
+ const handleSubmit = useCallback(() => {
+ if (isEmpty) return
+ landingSubmit(inputValue)
+ }, [isEmpty, inputValue, landingSubmit])
+
+ const handleKeyDown = useCallback(
+ (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault()
+ handleSubmit()
+ }
+ },
+ [handleSubmit]
+ )
+
+ const handleInput = useCallback((e: React.FormEvent) => {
+ const target = e.target as HTMLTextAreaElement
+ target.style.height = 'auto'
+ target.style.height = `${Math.min(target.scrollHeight, MAX_HEIGHT)}px`
+ }, [])
+
+ return (
+
+
+ What should we get done?
+
+
+
+
textareaRef.current?.focus()}
+ >
+
+
+
+
+
+ )
+}
diff --git a/apps/sim/app/(home)/components/footer/footer.tsx b/apps/sim/app/(home)/components/footer/footer.tsx
index 156f312945c..adde492497f 100644
--- a/apps/sim/app/(home)/components/footer/footer.tsx
+++ b/apps/sim/app/(home)/components/footer/footer.tsx
@@ -1,188 +1,165 @@
import Image from 'next/image'
import Link from 'next/link'
-import { FOOTER_BLOCKS, FOOTER_TOOLS } from '@/app/(landing)/components/footer/consts'
+import { FooterCTA } from '@/app/(home)/components/footer/footer-cta'
const LINK_CLASS = 'text-[14px] text-[#999] transition-colors hover:text-[#ECECEC]'
-interface FooterLink {
+interface FooterItem {
label: string
href: string
external?: boolean
}
-const FOOTER_LINKS: FooterLink[] = [
- { label: 'Docs', href: 'https://docs.sim.ai', external: true },
+const PRODUCT_LINKS: FooterItem[] = [
{ label: 'Pricing', href: '#pricing' },
{ label: 'Enterprise', href: 'https://form.typeform.com/to/jqCO12pF', external: true },
- { label: 'Sim Studio', href: '/studio' },
- { label: 'Changelog', href: '/changelog' },
+ { label: 'Self Hosting', href: 'https://docs.sim.ai/self-hosting', external: true },
+ { label: 'MCP', href: 'https://docs.sim.ai/mcp', external: true },
{ label: 'Status', href: 'https://status.sim.ai', external: true },
+]
+
+const RESOURCES_LINKS: FooterItem[] = [
+ { label: 'Blog', href: '/blog' },
+ { label: 'Docs', href: 'https://docs.sim.ai', external: true },
{ label: 'Careers', href: 'https://jobs.ashbyhq.com/sim', external: true },
- { label: 'SOC2', href: 'https://trust.delve.co/sim-studio', external: true },
- { label: 'Privacy Policy', href: '/privacy', external: true },
- { label: 'Terms of Service', href: '/terms', external: true },
+ { label: 'Changelog', href: '/changelog' },
+]
+
+const BLOCK_LINKS: FooterItem[] = [
+ { label: 'Agent', href: 'https://docs.sim.ai/blocks/agent', external: true },
+ { label: 'Router', href: 'https://docs.sim.ai/blocks/router', external: true },
+ { label: 'Function', href: 'https://docs.sim.ai/blocks/function', external: true },
+ { label: 'Condition', href: 'https://docs.sim.ai/blocks/condition', external: true },
+ { label: 'API', href: 'https://docs.sim.ai/blocks/api', external: true },
+ { label: 'Workflow', href: 'https://docs.sim.ai/blocks/workflow', external: true },
+ { label: 'Parallel', href: 'https://docs.sim.ai/blocks/parallel', external: true },
+ { label: 'Guardrails', href: 'https://docs.sim.ai/blocks/guardrails', external: true },
+ { label: 'Evaluator', href: 'https://docs.sim.ai/blocks/evaluator', external: true },
+ { label: 'Loop', href: 'https://docs.sim.ai/blocks/loop', external: true },
]
-export default function Footer() {
+const INTEGRATION_LINKS: FooterItem[] = [
+ { label: 'Confluence', href: 'https://docs.sim.ai/tools/confluence', external: true },
+ { label: 'Slack', href: 'https://docs.sim.ai/tools/slack', external: true },
+ { label: 'GitHub', href: 'https://docs.sim.ai/tools/github', external: true },
+ { label: 'Gmail', href: 'https://docs.sim.ai/tools/gmail', external: true },
+ { label: 'HubSpot', href: 'https://docs.sim.ai/tools/hubspot', external: true },
+ { label: 'Salesforce', href: 'https://docs.sim.ai/tools/salesforce', external: true },
+ { label: 'Notion', href: 'https://docs.sim.ai/tools/notion', external: true },
+ { label: 'Google Drive', href: 'https://docs.sim.ai/tools/google_drive', external: true },
+ { label: 'Google Sheets', href: 'https://docs.sim.ai/tools/google_sheets', external: true },
+ { label: 'Supabase', href: 'https://docs.sim.ai/tools/supabase', external: true },
+ { label: 'Stripe', href: 'https://docs.sim.ai/tools/stripe', external: true },
+ { label: 'Jira', href: 'https://docs.sim.ai/tools/jira', external: true },
+ { label: 'Linear', href: 'https://docs.sim.ai/tools/linear', external: true },
+ { label: 'Airtable', href: 'https://docs.sim.ai/tools/airtable', external: true },
+ { label: 'Firecrawl', href: 'https://docs.sim.ai/tools/firecrawl', external: true },
+ { label: 'Pinecone', href: 'https://docs.sim.ai/tools/pinecone', external: true },
+ { label: 'Discord', href: 'https://docs.sim.ai/tools/discord', external: true },
+ { label: 'Microsoft Teams', href: 'https://docs.sim.ai/tools/microsoft_teams', external: true },
+ { label: 'Outlook', href: 'https://docs.sim.ai/tools/outlook', external: true },
+ { label: 'Telegram', href: 'https://docs.sim.ai/tools/telegram', external: true },
+]
+
+const SOCIAL_LINKS: FooterItem[] = [
+ { label: 'X (Twitter)', href: 'https://x.com/simdotai', external: true },
+ { label: 'LinkedIn', href: 'https://www.linkedin.com/company/simstudioai/', external: true },
+ { label: 'Discord', href: 'https://discord.gg/Hr4UWYEcTT', external: true },
+ { label: 'GitHub', href: 'https://github.com/simstudioai/sim', external: true },
+]
+
+const LEGAL_LINKS: FooterItem[] = [
+ { label: 'Terms of Service', href: '/terms' },
+ { label: 'Privacy Policy', href: '/privacy' },
+]
+
+function FooterColumn({ title, items }: { title: string; items: FooterItem[] }) {
return (
-
-
-
- {/* Brand column */}
-
-
-
+
+
{title}
+
+ {items.map(({ label, href, external }) =>
+ external ? (
+
+ {label}
+
+ ) : (
+
+ {label}
-
-
- {/* Community column */}
-
+ )
+ )}
+
+
+ )
+}
- {/* Links column */}
-
-
More Sim
-
- {FOOTER_LINKS.map(({ label, href, external }) =>
- external ? (
-
- {label}
-
- ) : (
-
- {label}
-
- )
- )}
-
-
+interface FooterProps {
+ hideCTA?: boolean
+}
- {/* Blocks column */}
-
-
Blocks
-
- {FOOTER_BLOCKS.map((block) => (
-
- {block}
-
- ))}
+export default function Footer({ hideCTA }: FooterProps) {
+ return (
+
)
diff --git a/apps/sim/app/(home)/components/hero/hero.tsx b/apps/sim/app/(home)/components/hero/hero.tsx
index 9d9e8e4d102..d6c75765ca1 100644
--- a/apps/sim/app/(home)/components/hero/hero.tsx
+++ b/apps/sim/app/(home)/components/hero/hero.tsx
@@ -34,7 +34,7 @@ export default function Hero() {
Sim is the open-source platform to build AI agents and run your agentic workforce. Connect
@@ -61,11 +61,11 @@ export default function Hero() {
Build AI Agents
-
+
Sim is the AI Workspace for Agent Builders.
diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx
index 3fc28663318..3c6a7e47030 100644
--- a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx
+++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx
@@ -14,6 +14,7 @@ import {
GmailIcon,
GoogleCalendarIcon,
GoogleSheetsIcon,
+ HubspotIcon,
JiraIcon,
LinearIcon,
LinkedInIcon,
@@ -22,6 +23,7 @@ import {
OpenAIIcon,
RedditIcon,
ReductoIcon,
+ SalesforceIcon,
ScheduleIcon,
SlackIcon,
StartIcon,
@@ -57,11 +59,13 @@ const BLOCK_ICONS: Record
> =
google_calendar: GoogleCalendarIcon,
gmail: GmailIcon,
google_sheets: GoogleSheetsIcon,
+ hubspot: HubspotIcon,
linear: LinearIcon,
firecrawl: FirecrawlIcon,
reddit: RedditIcon,
notion: NotionIcon,
reducto: ReductoIcon,
+ salesforce: SalesforceIcon,
textract: TextractIcon,
linkedin: LinkedInIcon,
mothership: Blimp,
diff --git a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/workflow-data.ts b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/workflow-data.ts
index 205aa44d881..6f89683801d 100644
--- a/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/workflow-data.ts
+++ b/apps/sim/app/(home)/components/landing-preview/components/landing-preview-workflow/workflow-data.ts
@@ -112,10 +112,14 @@ const SELF_HEALING_CRM_WORKFLOW: PreviewWorkflow = {
},
{
id: 'mothership-1',
- name: 'Update Agent',
+ name: 'CRM Agent',
type: 'mothership',
bgColor: '#33C482',
rows: [{ title: 'Prompt', value: 'Audit CRM records, fix...' }],
+ tools: [
+ { name: 'HubSpot', type: 'hubspot', bgColor: '#FF7A59' },
+ { name: 'Salesforce', type: 'salesforce', bgColor: '#E0E0E0' },
+ ],
position: { x: 420, y: 180 },
hideSourceHandle: true,
},
diff --git a/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx b/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx
index 04695537e37..91ef0a2a4d3 100644
--- a/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx
+++ b/apps/sim/app/(home)/components/landing-preview/landing-preview.tsx
@@ -95,7 +95,7 @@ export function LandingPreview() {
onSelectHome={handleSelectHome}
/>
-
+
+
+
+
+
+
+ {title}
+
+
+
+ )
+}
+
+export function BlogDropdown() {
+ return (
+
+
+
+
+ {POSTS.slice(0, 2).map((post) => (
+
+ ))}
+
+ {POSTS.slice(2).map((post) => (
+
+ ))}
+
+
+ )
+}
diff --git a/apps/sim/app/(home)/components/navbar/components/docs-dropdown.tsx b/apps/sim/app/(home)/components/navbar/components/docs-dropdown.tsx
new file mode 100644
index 00000000000..9d5daa4fa2f
--- /dev/null
+++ b/apps/sim/app/(home)/components/navbar/components/docs-dropdown.tsx
@@ -0,0 +1,92 @@
+import { AgentIcon, GithubOutlineIcon, McpIcon } from '@/components/icons'
+
+const PREVIEW_CARDS = [
+ {
+ title: 'Introduction',
+ href: 'https://docs.sim.ai',
+ image: '/landing/docs-getting-started.svg',
+ },
+ {
+ title: 'Getting Started',
+ href: 'https://docs.sim.ai/getting-started',
+ image: '/landing/docs-intro.svg',
+ },
+] as const
+
+const RESOURCE_CARDS = [
+ {
+ title: 'Agent',
+ description: 'Build AI agents',
+ href: 'https://docs.sim.ai/blocks/agent',
+ icon: AgentIcon,
+ },
+ {
+ title: 'MCP',
+ description: 'Connect tools',
+ href: 'https://docs.sim.ai/mcp',
+ icon: McpIcon,
+ },
+ {
+ title: 'Self-hosting',
+ description: 'Host on your infra',
+ href: 'https://docs.sim.ai/self-hosting',
+ icon: GithubOutlineIcon,
+ },
+] as const
+
+export function DocsDropdown() {
+ return (
+
+ )
+}
diff --git a/apps/sim/app/(home)/components/navbar/navbar.tsx b/apps/sim/app/(home)/components/navbar/navbar.tsx
index a411a65f78c..17337b6e73f 100644
--- a/apps/sim/app/(home)/components/navbar/navbar.tsx
+++ b/apps/sim/app/(home)/components/navbar/navbar.tsx
@@ -1,27 +1,33 @@
+'use client'
+
+import { useCallback, useEffect, useRef, useState } from 'react'
import Image from 'next/image'
import Link from 'next/link'
-import { ChevronDown } from '@/components/emcn'
+import { GithubOutlineIcon } from '@/components/icons'
+import { cn } from '@/lib/core/utils/cn'
+import { BlogDropdown } from '@/app/(home)/components/navbar/components/blog-dropdown'
+import { DocsDropdown } from '@/app/(home)/components/navbar/components/docs-dropdown'
import { GitHubStars } from '@/app/(home)/components/navbar/components/github-stars'
import { getBrandConfig } from '@/ee/whitelabeling'
+type DropdownId = 'docs' | 'blog' | null
+
interface NavLink {
label: string
href: string
external?: boolean
icon?: 'chevron'
+ dropdown?: 'docs' | 'blog'
}
const NAV_LINKS: NavLink[] = [
- { label: 'Docs', href: 'https://docs.sim.ai', external: true },
- { label: 'Pricing', href: '/pricing' },
- { label: 'Careers', href: '/careers' },
+ { label: 'Docs', href: 'https://docs.sim.ai', external: true, icon: 'chevron', dropdown: 'docs' },
+ { label: 'Blog', href: '/blog', icon: 'chevron', dropdown: 'blog' },
+ { label: 'Pricing', href: '#pricing' },
{ label: 'Enterprise', href: 'https://form.typeform.com/to/jqCO12pF', external: true },
]
-/** Logo and nav edge: horizontal padding (px) for left/right symmetry. */
-const LOGO_CELL = 'flex items-center px-[20px]'
-
-/** Links: even spacing between items. */
+const LOGO_CELL = 'flex items-center pl-[20px] lg:pl-[80px] pr-[20px]'
const LINK_CELL = 'flex items-center px-[14px]'
interface NavbarProps {
@@ -30,15 +36,58 @@ interface NavbarProps {
export default function Navbar({ logoOnly = false }: NavbarProps) {
const brand = getBrandConfig()
+ const [activeDropdown, setActiveDropdown] = useState
(null)
+ const [hoveredLink, setHoveredLink] = useState(null)
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
+ const closeTimerRef = useRef | null>(null)
+
+ const openDropdown = useCallback((id: DropdownId) => {
+ if (closeTimerRef.current) {
+ clearTimeout(closeTimerRef.current)
+ closeTimerRef.current = null
+ }
+ setActiveDropdown(id)
+ }, [])
+
+ const scheduleClose = useCallback(() => {
+ if (closeTimerRef.current) clearTimeout(closeTimerRef.current)
+ closeTimerRef.current = setTimeout(() => {
+ setActiveDropdown(null)
+ closeTimerRef.current = null
+ }, 100)
+ }, [])
+
+ useEffect(() => {
+ return () => {
+ if (closeTimerRef.current) clearTimeout(closeTimerRef.current)
+ }
+ }, [])
+
+ useEffect(() => {
+ document.body.style.overflow = mobileMenuOpen ? 'hidden' : ''
+ return () => {
+ document.body.style.overflow = ''
+ }
+ }, [mobileMenuOpen])
+
+ useEffect(() => {
+ const mq = window.matchMedia('(min-width: 1024px)')
+ const handler = () => {
+ if (mq.matches) setMobileMenuOpen(false)
+ }
+ mq.addEventListener('change', handler)
+ return () => mq.removeEventListener('change', handler)
+ }, [])
+
+ const anyHighlighted = activeDropdown !== null || hoveredLink !== null
return (
- {/* Logo */}
{brand.name}
@@ -67,37 +116,93 @@ export default function Navbar({ logoOnly = false }: NavbarProps) {
{!logoOnly && (
<>
- {/* Links */}
-
-
+
- {/* CTAs */}
-
+
+
+
+ setMobileMenuOpen((prev) => !prev)}
+ aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
+ aria-expanded={mobileMenuOpen}
+ >
+
+
+
+
+
+
+
+
+ setMobileMenuOpen(false)}
+ aria-label='Log in'
+ >
+ Log in
+
+ setMobileMenuOpen(false)}
+ aria-label='Get started with Sim'
+ >
+ Get started
+
+
+
>
)}
)
}
+
+interface NavChevronProps {
+ open: boolean
+}
+
+/**
+ * Animated chevron matching the exact geometry of the emcn ChevronDown SVG.
+ * Each arm rotates around its midpoint so the center vertex travels up/down
+ * while the outer endpoints adjust — producing a Stripe-style morph.
+ */
+function NavChevron({ open }: NavChevronProps) {
+ return (
+
+
+
+
+ )
+}
+
+function MobileMenuIcon({ open }: { open: boolean }) {
+ if (open) {
+ return (
+
+
+
+ )
+ }
+ return (
+
+
+
+ )
+}
+
+function ExternalArrowIcon() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/sim/app/(home)/components/pricing/pricing.tsx b/apps/sim/app/(home)/components/pricing/pricing.tsx
index 25e34505fe6..89272b58e83 100644
--- a/apps/sim/app/(home)/components/pricing/pricing.tsx
+++ b/apps/sim/app/(home)/components/pricing/pricing.tsx
@@ -174,7 +174,7 @@ function PricingCard({ tier }: PricingCardProps) {
export default function Pricing() {
return (
-
+
-
+
{PRICING_TIERS.map((tier) => (
))}
diff --git a/apps/sim/app/(home)/components/templates/templates.tsx b/apps/sim/app/(home)/components/templates/templates.tsx
index 3625501407f..f38d69aba58 100644
--- a/apps/sim/app/(home)/components/templates/templates.tsx
+++ b/apps/sim/app/(home)/components/templates/templates.tsx
@@ -1,8 +1,8 @@
'use client'
-import { useCallback, useRef, useState } from 'react'
+import { useCallback, useEffect, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
-import { type MotionValue, motion, useScroll, useTransform } from 'framer-motion'
+import { AnimatePresence, type MotionValue, motion, useScroll, useTransform } from 'framer-motion'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/navigation'
import { Badge, ChevronDown } from '@/components/emcn'
@@ -349,8 +349,17 @@ export default function Templates() {
const sectionRef = useRef
(null)
const [activeIndex, setActiveIndex] = useState(0)
const [isPreparingTemplate, setIsPreparingTemplate] = useState(false)
+ const [isMobile, setIsMobile] = useState(false)
const router = useRouter()
+ useEffect(() => {
+ const mq = window.matchMedia('(max-width: 1023px)')
+ setIsMobile(mq.matches)
+ const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches)
+ mq.addEventListener('change', handler)
+ return () => mq.removeEventListener('change', handler)
+ }, [])
+
const { scrollYProgress } = useScroll({
target: sectionRef,
offset: ['start 0.9', 'start 0.2'],
@@ -415,8 +424,8 @@ export default function Templates() {
@@ -440,7 +449,7 @@ export default function Templates() {
-
+
Ship your agent in minutes
-
- Pre-built templates for every use case—pick one, swap
+
+ Pre-built templates for every use case—pick one, swap{' '}
+
models and tools to fit your stack, and deploy.
-
-
+
+
-
+
{TEMPLATE_WORKFLOWS.map((workflow, index) => {
const isActive = index === activeIndex
return (
-
setActiveIndex(index)}
- className={cn(
- 'relative text-left',
- isActive
- ? 'z-10'
- : 'flex items-center px-[12px] py-[10px] shadow-[inset_0_-1px_0_0_#2A2A2A] last:shadow-none hover:bg-[#232323]/50'
- )}
- >
- {isActive ? (
- (() => {
- const depth = DEPTH_CONFIGS[workflow.id]
- return (
- <>
-
-
-
-
- {workflow.name}
-
-
+ setActiveIndex(index)}
+ className={cn(
+ 'relative w-full text-left',
+ isActive
+ ? 'z-10'
+ : cn(
+ 'flex items-center px-[12px] py-[10px] hover:bg-[#232323]/50',
+ index < TEMPLATE_WORKFLOWS.length - 1 &&
+ 'shadow-[inset_0_-1px_0_0_#2A2A2A]'
+ )
+ )}
+ >
+ {isActive ? (
+ (() => {
+ const depth = DEPTH_CONFIGS[workflow.id]
+ return (
+ <>
+
+
-
- >
- )
- })()
- ) : (
-
- {workflow.name}
-
- )}
-
+
+
+ {workflow.name}
+
+
+
+ >
+ )
+ })()
+ ) : (
+
+ {workflow.name}
+
+ )}
+
+
+
+ {isActive && isMobile && (
+
+
+
+
+
+
+ {isPreparingTemplate ? 'Preparing...' : 'Use template'}
+
+
+
+ )}
+
+
)
})}
@@ -582,12 +639,24 @@ export default function Templates() {
-
+
diff --git a/apps/sim/app/(landing)/studio/[slug]/back-link.tsx b/apps/sim/app/(landing)/blog/[slug]/back-link.tsx
similarity index 94%
rename from apps/sim/app/(landing)/studio/[slug]/back-link.tsx
rename to apps/sim/app/(landing)/blog/[slug]/back-link.tsx
index c75dd934092..179223a0b8a 100644
--- a/apps/sim/app/(landing)/studio/[slug]/back-link.tsx
+++ b/apps/sim/app/(landing)/blog/[slug]/back-link.tsx
@@ -9,7 +9,7 @@ export function BackLink() {
return (
setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
@@ -21,7 +21,7 @@ export function BackLink() {
)}
- Back to Sim Studio
+ Back to Blog
)
}
diff --git a/apps/sim/app/(landing)/studio/[slug]/page.tsx b/apps/sim/app/(landing)/blog/[slug]/page.tsx
similarity index 95%
rename from apps/sim/app/(landing)/studio/[slug]/page.tsx
rename to apps/sim/app/(landing)/blog/[slug]/page.tsx
index 8b49912ea06..d5ed263e2b5 100644
--- a/apps/sim/app/(landing)/studio/[slug]/page.tsx
+++ b/apps/sim/app/(landing)/blog/[slug]/page.tsx
@@ -6,8 +6,8 @@ import { FAQ } from '@/lib/blog/faq'
import { getAllPostMeta, getPostBySlug, getRelatedPosts } from '@/lib/blog/registry'
import { buildArticleJsonLd, buildBreadcrumbJsonLd, buildPostMetadata } from '@/lib/blog/seo'
import { getBaseUrl } from '@/lib/core/utils/urls'
-import { BackLink } from '@/app/(landing)/studio/[slug]/back-link'
-import { ShareButton } from '@/app/(landing)/studio/[slug]/share-button'
+import { BackLink } from '@/app/(landing)/blog/[slug]/back-link'
+import { ShareButton } from '@/app/(landing)/blog/[slug]/share-button'
export async function generateStaticParams() {
const posts = await getAllPostMeta()
@@ -95,7 +95,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
))}
-
+
@@ -134,7 +134,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
Related posts
{related.map((p) => (
-
+
{posts.map((p) => (
-
+
@@ -36,11 +36,11 @@ export default async function StudioIndex({
const posts = sorted.slice(start, start + perPage)
// Tag filter chips are intentionally disabled for now.
// const tags = await getAllTags()
- const studioJsonLd = {
+ const blogJsonLd = {
'@context': 'https://schema.org',
'@type': 'Blog',
- name: 'Sim Studio',
- url: 'https://sim.ai/studio',
+ name: 'Sim Blog',
+ url: 'https://sim.ai/blog',
description: 'Announcements, insights, and guides for building AI agent workflows.',
}
@@ -48,10 +48,10 @@ export default async function StudioIndex({
- Sim Studio
+ Blog
Announcements, insights, and guides for building AI agent workflows.
@@ -59,9 +59,9 @@ export default async function StudioIndex({
{/* Tag filter chips hidden until we have more posts */}
{/*
-
All
+
All
{tags.map((t) => (
-
+
{t.tag} ({t.count})
))}
@@ -74,7 +74,7 @@ export default async function StudioIndex({
{pageNum > 1 && (
Previous
@@ -85,7 +85,7 @@ export default async function StudioIndex({
{pageNum < totalPages && (
Next
diff --git a/apps/sim/app/(landing)/studio/post-grid.tsx b/apps/sim/app/(landing)/blog/post-grid.tsx
similarity index 97%
rename from apps/sim/app/(landing)/studio/post-grid.tsx
rename to apps/sim/app/(landing)/blog/post-grid.tsx
index bb90b7328c3..918d6782f53 100644
--- a/apps/sim/app/(landing)/studio/post-grid.tsx
+++ b/apps/sim/app/(landing)/blog/post-grid.tsx
@@ -26,7 +26,7 @@ export function PostGrid({ posts }: { posts: Post[] }) {
return (
{posts.map((p, index) => (
-
+
{/* Image container with fixed aspect ratio to prevent layout shift */}
diff --git a/apps/sim/app/(landing)/studio/rss.xml/route.ts b/apps/sim/app/(landing)/blog/rss.xml/route.ts
similarity index 97%
rename from apps/sim/app/(landing)/studio/rss.xml/route.ts
rename to apps/sim/app/(landing)/blog/rss.xml/route.ts
index 348557c1246..c57975aa007 100644
--- a/apps/sim/app/(landing)/studio/rss.xml/route.ts
+++ b/apps/sim/app/(landing)/blog/rss.xml/route.ts
@@ -11,7 +11,7 @@ export async function GET() {
const xml = `
- Sim Studio
+ Sim Blog
${site}
Announcements, insights, and guides for AI agent workflows.
${items
diff --git a/apps/sim/app/(landing)/studio/sitemap-images.xml/route.ts b/apps/sim/app/(landing)/blog/sitemap-images.xml/route.ts
similarity index 100%
rename from apps/sim/app/(landing)/studio/sitemap-images.xml/route.ts
rename to apps/sim/app/(landing)/blog/sitemap-images.xml/route.ts
diff --git a/apps/sim/app/(landing)/studio/tags/page.tsx b/apps/sim/app/(landing)/blog/tags/page.tsx
similarity index 91%
rename from apps/sim/app/(landing)/studio/tags/page.tsx
rename to apps/sim/app/(landing)/blog/tags/page.tsx
index 0f5066dc434..a36ca97fd05 100644
--- a/apps/sim/app/(landing)/studio/tags/page.tsx
+++ b/apps/sim/app/(landing)/blog/tags/page.tsx
@@ -13,7 +13,7 @@ export default async function TagsIndex() {
Browse by tag
All
@@ -21,7 +21,7 @@ export default async function TagsIndex() {
{tags.map((t) => (
{t.tag} ({t.count})
diff --git a/apps/sim/app/(landing)/components/footer/footer.tsx b/apps/sim/app/(landing)/components/footer/footer.tsx
index 9bc672c9bd3..9ed8d03c84a 100644
--- a/apps/sim/app/(landing)/components/footer/footer.tsx
+++ b/apps/sim/app/(landing)/components/footer/footer.tsx
@@ -57,10 +57,10 @@ export default function Footer({ fullWidth = false }: FooterProps) {
Enterprise
- Sim Studio
+ Blog
- {isHosted && }
+ {isHosted && }
)
}
diff --git a/apps/sim/app/_shell/providers/theme-provider.tsx b/apps/sim/app/_shell/providers/theme-provider.tsx
index b4ad531e590..43a6f0af2b5 100644
--- a/apps/sim/app/_shell/providers/theme-provider.tsx
+++ b/apps/sim/app/_shell/providers/theme-provider.tsx
@@ -20,7 +20,7 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
pathname.startsWith('/verify') ||
pathname.startsWith('/changelog') ||
pathname.startsWith('/chat') ||
- pathname.startsWith('/studio') ||
+ pathname.startsWith('/blog') ||
pathname.startsWith('/resume') ||
pathname.startsWith('/form') ||
pathname.startsWith('/oauth')
diff --git a/apps/sim/app/changelog/layout.tsx b/apps/sim/app/changelog/layout.tsx
index 2714b3a51f1..e7d7153dfca 100644
--- a/apps/sim/app/changelog/layout.tsx
+++ b/apps/sim/app/changelog/layout.tsx
@@ -11,7 +11,7 @@ export default function ChangelogLayout({ children }: { children: React.ReactNod
{children}
-
+
)
}
diff --git a/apps/sim/app/llms.txt/route.ts b/apps/sim/app/llms.txt/route.ts
index e38d203c1b0..d604ff443fc 100644
--- a/apps/sim/app/llms.txt/route.ts
+++ b/apps/sim/app/llms.txt/route.ts
@@ -14,7 +14,7 @@ Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over
- [Homepage](${baseUrl}): Product overview, features, and pricing
- [Templates](${baseUrl}/templates): Pre-built workflow templates to get started quickly
- [Changelog](${baseUrl}/changelog): Product updates and release notes
-- [Sim Studio Blog](${baseUrl}/studio): Announcements, insights, and guides
+- [Sim Blog](${baseUrl}/blog): Announcements, insights, and guides
## Documentation
diff --git a/apps/sim/app/not-found.tsx b/apps/sim/app/not-found.tsx
index 4715d5079f4..90be7c5df3f 100644
--- a/apps/sim/app/not-found.tsx
+++ b/apps/sim/app/not-found.tsx
@@ -1,28 +1,33 @@
-'use client'
-
-import { useRouter } from 'next/navigation'
+import Link from 'next/link'
import AuthBackground from '@/app/(auth)/components/auth-background'
-import { BrandedButton } from '@/app/(auth)/components/branded-button'
import Navbar from '@/app/(home)/components/navbar/navbar'
-export default function NotFound() {
- const router = useRouter()
+const CTA_BASE =
+ 'inline-flex items-center h-[32px] rounded-[5px] border px-[10px] font-[430] font-season text-[14px]'
+export default function NotFound() {
return (
-
-
Page Not Found
-
- The page you're looking for doesn't exist or has been moved.
-
-
-
router.push('/')} showArrow={false}>
- Return to Home
-
+
+
+
+ Page Not Found
+
+
+ The page you're looking for doesn't exist or has been moved.
+
+
+
+ Return to Home
+
+
diff --git a/apps/sim/app/sitemap.ts b/apps/sim/app/sitemap.ts
index 2c6da9db7dd..3472ff961be 100644
--- a/apps/sim/app/sitemap.ts
+++ b/apps/sim/app/sitemap.ts
@@ -13,11 +13,11 @@ export default async function sitemap(): Promise
{
lastModified: now,
},
{
- url: `${baseUrl}/studio`,
+ url: `${baseUrl}/blog`,
lastModified: now,
},
{
- url: `${baseUrl}/studio/tags`,
+ url: `${baseUrl}/blog/tags`,
lastModified: now,
},
{
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx
index 5cdb9c6a789..c48bec55ab7 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group.tsx
@@ -58,7 +58,7 @@ export function AgentGroup({
}, [expanded])
return (
-
+
{hasItems ? (
@@ -98,14 +98,15 @@ export function AgentGroup({
toolName={item.data.toolName}
displayTitle={item.data.displayTitle}
status={item.data.status}
+ result={item.data.result}
/>
) : (
-
{item.content.trim()}
-
+
)
)}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx
index e4b2c68a24c..2c683cd8097 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item.tsx
@@ -1,7 +1,29 @@
-import { Loader } from '@/components/emcn'
-import type { ToolCallStatus } from '../../../../types'
+'use client'
+
+import { useMemo } from 'react'
+import { PillsRing } from '@/components/emcn'
+import type { ToolCallResult, ToolCallStatus } from '../../../../types'
import { getToolIcon } from '../../utils'
+/** Tools that render as cards with result data on success. */
+const CARD_TOOLS = new Set
([
+ 'function_execute',
+ 'search_online',
+ 'scrape_page',
+ 'get_page_contents',
+ 'search_library_docs',
+ 'superagent',
+ 'run',
+ 'plan',
+ 'debug',
+ 'edit',
+ 'fast_edit',
+ 'custom_tool',
+ 'research',
+ 'agent',
+ 'job',
+])
+
function CircleCheck({ className }: { className?: string }) {
return (
+ }
+ if (status === 'cancelled') {
+ return
+ }
+ const Icon = getToolIcon(toolName)
+ if (Icon) {
+ return
+ }
+ return
+}
+
+function FlatToolLine({
+ toolName,
+ displayTitle,
+ status,
+}: {
+ toolName: string
+ displayTitle: string
+ status: ToolCallStatus
+}) {
+ return (
+
+ )
+}
+
+function formatToolOutput(output: unknown): string {
+ if (output === null || output === undefined) return ''
+ if (typeof output === 'string') return output
+ try {
+ return JSON.stringify(output, null, 2)
+ } catch {
+ return String(output)
+ }
+}
+
interface ToolCallItemProps {
toolName: string
displayTitle: string
status: ToolCallStatus
+ result?: ToolCallResult
+}
+
+export function ToolCallItem({ toolName, displayTitle, status, result }: ToolCallItemProps) {
+ const showCard =
+ CARD_TOOLS.has(toolName) &&
+ status === 'success' &&
+ result?.output !== undefined &&
+ result?.output !== null
+
+ if (showCard) {
+ return
+ }
+
+ return
}
-export function ToolCallItem({ toolName, displayTitle, status }: ToolCallItemProps) {
+function ToolCallCard({
+ toolName,
+ displayTitle,
+ result,
+}: {
+ toolName: string
+ displayTitle: string
+ result: ToolCallResult
+}) {
+ const body = useMemo(() => formatToolOutput(result.output), [result.output])
const Icon = getToolIcon(toolName)
+ const ResolvedIcon = Icon ?? CircleCheck
return (
-
-
- {status === 'executing' ? (
-
- ) : status === 'cancelled' ? (
-
- ) : Icon ? (
-
- ) : (
-
+
)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx
index 082224f2cdb..eb3db2357ae 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx
@@ -216,9 +216,9 @@ export function ChatContent({ content, isStreaming = false, onOptionSelect }: Ch
return (
{parsed.segments.map((segment, i) => {
- if (segment.type === 'text') {
+ if (segment.type === 'text' || segment.type === 'thinking') {
return (
-
+
{segment.content}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
index e20b5ed8766..7a3d98df50f 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx
@@ -27,6 +27,7 @@ export interface CredentialTagData {
export type ContentSegment =
| { type: 'text'; content: string }
+ | { type: 'thinking'; content: string }
| { type: 'options'; data: OptionsTagData }
| { type: 'usage_upgrade'; data: UsageUpgradeTagData }
| { type: 'credential'; data: CredentialTagData }
@@ -36,7 +37,7 @@ export interface ParsedSpecialContent {
hasPendingTag: boolean
}
-const SPECIAL_TAG_NAMES = ['options', 'usage_upgrade', 'credential'] as const
+const SPECIAL_TAG_NAMES = ['thinking', 'options', 'usage_upgrade', 'credential'] as const
/**
* Parses inline special tags (`
`, ``) from streamed
@@ -103,11 +104,17 @@ export function parseSpecialTags(content: string, isStreaming: boolean): ParsedS
}
const body = content.slice(bodyStart, closeIdx)
- try {
- const data = JSON.parse(body)
- segments.push({ type: nearestTagName as 'options' | 'usage_upgrade' | 'credential', data })
- } catch {
- /* malformed JSON — drop the tag silently */
+ if (nearestTagName === 'thinking') {
+ if (body.trim()) {
+ segments.push({ type: 'thinking', content: body })
+ }
+ } else {
+ try {
+ const data = JSON.parse(body)
+ segments.push({ type: nearestTagName as 'options' | 'usage_upgrade' | 'credential', data })
+ } catch {
+ /* malformed JSON — drop the tag silently */
+ }
}
cursor = closeIdx + closeTag.length
@@ -137,6 +144,8 @@ interface SpecialTagsProps {
*/
export function SpecialTags({ segment, onOptionSelect }: SpecialTagsProps) {
switch (segment.type) {
+ case 'thinking':
+ return null
case 'options':
return
case 'usage_upgrade':
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx
index d4f74304782..c3503ad30ac 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/message-content.tsx
@@ -3,7 +3,7 @@
import type { ContentBlock, OptionItem, SubagentName, ToolCallData } from '../../types'
import { SUBAGENT_LABELS } from '../../types'
import type { AgentGroupItem } from './components'
-import { AgentGroup, ChatContent, CircleStop, Options } from './components'
+import { AgentGroup, ChatContent, CircleStop, Options, PendingTagIndicator } from './components'
interface TextSegment {
type: 'text'
@@ -49,6 +49,7 @@ function toToolData(tc: NonNullable): ToolCallData {
toolName: tc.name,
displayTitle: tc.displayTitle || formatToolName(tc.name),
status: tc.status,
+ result: tc.result,
}
}
@@ -78,6 +79,15 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
if (block.type === 'text') {
if (!block.content?.trim()) continue
+ if (block.subagent && group && group.agentName === block.subagent) {
+ const lastItem = group.items[group.items.length - 1]
+ if (lastItem?.type === 'text') {
+ lastItem.content += block.content
+ } else {
+ group.items.push({ type: 'text', content: block.content })
+ }
+ continue
+ }
if (group) {
segments.push(group)
group = null
@@ -177,6 +187,14 @@ function parseBlocks(blocks: ContentBlock[]): MessageSegment[] {
continue
}
+ if (block.type === 'subagent_end') {
+ if (group) {
+ segments.push(group)
+ group = null
+ }
+ continue
+ }
+
if (block.type === 'stopped') {
if (group) {
segments.push(group)
@@ -214,6 +232,27 @@ export function MessageContent({
if (segments.length === 0) return null
+ const lastSegment = segments[segments.length - 1]
+ const hasTrailingContent = lastSegment.type === 'text' || lastSegment.type === 'stopped'
+
+ let allLastGroupToolsDone = false
+ if (lastSegment.type === 'agent_group') {
+ const toolItems = lastSegment.items.filter((item) => item.type === 'tool')
+ allLastGroupToolsDone =
+ toolItems.length > 0 &&
+ toolItems.every(
+ (t) =>
+ t.type === 'tool' &&
+ (t.data.status === 'success' ||
+ t.data.status === 'error' ||
+ t.data.status === 'cancelled')
+ )
+ }
+
+ const hasSubagentEnded = blocks.some((b) => b.type === 'subagent_end')
+ const showTrailingThinking =
+ isStreaming && !hasTrailingContent && (hasSubagentEnded || allLastGroupToolsDone)
+
return (
{segments.map((segment, i) => {
@@ -270,6 +309,11 @@ export function MessageContent({
)
}
})}
+ {showTrailingThinking && (
+
+ )}
)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts
index b7dbdaef2e0..94fad449bec 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/utils.ts
@@ -2,7 +2,6 @@ import type { ComponentType, SVGProps } from 'react'
import {
Asterisk,
Blimp,
- BubbleChatPreview,
Bug,
Calendar,
ClipboardList,
@@ -23,6 +22,7 @@ import {
Wrench,
} from '@/components/emcn'
import { Table as TableIcon } from '@/components/emcn/icons'
+import { AgentIcon } from '@/components/icons'
import type { MothershipToolName, SubagentName } from '../../types'
export type IconComponent = ComponentType>
@@ -53,7 +53,7 @@ const TOOL_ICONS: Record
+
setIsExpanded(!isExpanded)}
- className='flex w-full items-center gap-[6px] px-[14px] py-[8px] transition-colors hover:bg-black/[0.03] dark:hover:bg-white/[0.03]'
+ className='flex w-full items-center gap-[6px] px-[14px] py-[8px] transition-colors hover:bg-[var(--surface-active)]'
>
{isExpanded ? (
-
+
) : (
-
+
)}
{messageQueue.length} Queued
@@ -39,7 +39,7 @@ export function QueuedMessages({ messageQueue, onRemove, onSendNow, onEdit }: Qu
{messageQueue.map((msg) => (
@@ -58,7 +58,7 @@ export function QueuedMessages({ messageQueue, onRemove, onSendNow, onEdit }: Qu
e.stopPropagation()
onEdit(msg.id)
}}
- className='rounded-[6px] p-[5px] text-[var(--text-tertiary)] transition-colors hover:bg-black/[0.06] hover:text-[var(--text-primary)] dark:hover:bg-white/[0.06]'
+ className='rounded-[6px] p-[5px] text-[var(--text-icon)] transition-colors hover:bg-[var(--surface-active)] hover:text-[var(--text-primary)]'
>
@@ -76,7 +76,7 @@ export function QueuedMessages({ messageQueue, onRemove, onSendNow, onEdit }: Qu
e.stopPropagation()
void onSendNow(msg.id)
}}
- className='rounded-[6px] p-[5px] text-[var(--text-tertiary)] transition-colors hover:bg-black/[0.06] hover:text-[var(--text-primary)] dark:hover:bg-white/[0.06]'
+ className='rounded-[6px] p-[5px] text-[var(--text-icon)] transition-colors hover:bg-[var(--surface-active)] hover:text-[var(--text-primary)]'
>
@@ -94,7 +94,7 @@ export function QueuedMessages({ messageQueue, onRemove, onSendNow, onEdit }: Qu
e.stopPropagation()
onRemove(msg.id)
}}
- className='rounded-[6px] p-[5px] text-[var(--text-tertiary)] transition-colors hover:bg-black/[0.06] hover:text-[var(--text-primary)] dark:hover:bg-white/[0.06]'
+ className='rounded-[6px] p-[5px] text-[var(--text-icon)] transition-colors hover:bg-[var(--surface-active)] hover:text-[var(--text-primary)]'
>
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/consts.ts b/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/consts.ts
index 0f99b81fc2e..fd7425794a6 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/consts.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/consts.ts
@@ -11,7 +11,6 @@ import {
Hammer,
Integration,
Layout,
- Library,
Mail,
Pencil,
Rocket,
@@ -199,7 +198,7 @@ export const TEMPLATES: TemplatePrompt[] = [
tags: ['sales', 'content', 'enterprise'],
},
{
- icon: Library,
+ icon: File,
title: 'Competitive battle cards',
prompt:
'Create an agent that deep-researches each of my competitors using web search — their product features, pricing, positioning, strengths, and weaknesses — and generates a structured battle card document for each one that my sales team can reference during calls.',
@@ -830,7 +829,7 @@ export const TEMPLATES: TemplatePrompt[] = [
tags: ['hr', 'automation', 'team'],
},
{
- icon: Library,
+ icon: ClipboardList,
title: 'Candidate screening assistant',
prompt:
'Create a knowledge base from my job descriptions and hiring criteria, then build a workflow that takes uploaded resumes, evaluates candidates against the requirements, scores them on experience, skills, and culture fit, and populates a comparison table with a summary and recommendation for each.',
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/template-prompts.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/template-prompts.tsx
index e5f97897de0..c0057396e26 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/template-prompts.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/template-prompts/template-prompts.tsx
@@ -1,15 +1,14 @@
'use client'
-import { useState } from 'react'
+import { type ComponentType, memo, type SVGProps } from 'react'
import Image from 'next/image'
-import { ChevronDown } from '@/components/emcn/icons'
+import { AgentIcon, ScheduleIcon, StartIcon } from '@/components/icons'
import type { Category, ModuleTag } from './consts'
-import { CATEGORY_META, MODULE_META, TEMPLATES } from './consts'
+import { CATEGORY_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
()
@@ -38,19 +37,280 @@ function getGroupedExtras() {
const GROUPED_EXTRAS = getGroupedExtras()
-function ModulePills({ modules }: { modules: ModuleTag[] }) {
+const MINI_TABLE_DATA = [
+ ['Sarah Chen', 'sarah@acme.co', 'Acme Inc', 'Qualified'],
+ ['James Park', 'james@globex.io', 'Globex', 'New'],
+ ['Maria Santos', 'maria@initech.com', 'Initech', 'Contacted'],
+ ['Alex Kim', 'alex@umbrella.co', 'Umbrella', 'Qualified'],
+ ['Emma Wilson', 'emma@stark.io', 'Stark Ind', 'New'],
+] as const
+
+const STATUS_DOT: Record = {
+ Qualified: 'bg-emerald-400',
+ New: 'bg-blue-400',
+ Contacted: 'bg-amber-400',
+}
+
+const MINI_KB_DATA = [
+ ['product-specs.pdf', '4.2 MB', '12.4k', 'Enabled'],
+ ['eng-handbook.md', '1.8 MB', '8.2k', 'Enabled'],
+ ['api-reference.json', '920 KB', '4.1k', 'Enabled'],
+ ['release-notes.md', '340 KB', '2.8k', 'Enabled'],
+ ['onboarding.pdf', '2.1 MB', '6.5k', 'Processing'],
+] as const
+
+const KB_BADGE: Record = {
+ Enabled: 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400',
+ Processing: 'bg-violet-500/15 text-violet-700 dark:text-violet-400',
+}
+
+interface WorkflowBlockDef {
+ color: string
+ name: string
+ icon: ComponentType>
+ rows: { title: string; value: string }[]
+}
+
+function PreviewTable() {
+ return (
+
+
+ {['Name', 'Email', 'Company', 'Status'].map((col) => (
+
+ {col}
+
+ ))}
+
+ {MINI_TABLE_DATA.map((row, i) => (
+
+ {row.map((cell, j) => (
+
+ {j === 3 ? (
+
+ ) : (
+
+ {cell}
+
+ )}
+
+ ))}
+
+ ))}
+
+ )
+}
+
+function PreviewKnowledge() {
+ return (
+
+
+ {['Name', 'Size', 'Tokens', 'Status'].map((col) => (
+
+ {col}
+
+ ))}
+
+ {MINI_KB_DATA.map((row, i) => (
+
+
+
+ {row[0]}
+
+
+
+ {row[1]}
+
+
+ {row[2]}
+
+
+
+ {row[3]}
+
+
+
+ ))}
+
+ )
+}
+
+function PreviewFile() {
+ return (
+
+
+ Files
+ /
+ meeting-notes.md
+
+
+
Meeting Notes
+
Action Items
+
+ • Review Q1 metrics with Sarah
+
+
• Update API documentation
+
+ • Schedule design review for v2.0
+
+
Discussion Points
+
+ The team agreed to prioritize the new onboarding flow...
+
+
Next Steps
+
+ Follow up with engineering on the API v2 migration.
+
+
+
+ )
+}
+
+const WorkflowMiniBlock = memo(function WorkflowMiniBlock({
+ color,
+ name,
+ icon: Icon,
+ rows,
+}: WorkflowBlockDef) {
+ const hasRows = rows.length > 0
return (
-
- {modules.map((mod) => (
-
+
+
- {MODULE_META[mod].label}
-
+
+
+
{name}
+
+ {rows.map((row) => (
+
+ {row.title}
+ {row.value}
+
))}
)
+})
+
+function buildWorkflowBlocks(template: (typeof TEMPLATES)[number]): WorkflowBlockDef[] {
+ const modules = template.modules
+ const toolName = template.title.split(' ')[0]
+ const hasAgent = modules.includes('agent')
+ const isScheduled = modules.includes('scheduled')
+
+ const starter: WorkflowBlockDef = isScheduled
+ ? {
+ color: '#6366F1',
+ name: 'Schedule',
+ icon: ScheduleIcon,
+ rows: [{ title: 'Cron', value: '0 9 * * 1' }],
+ }
+ : {
+ color: '#2FB3FF',
+ name: 'Starter',
+ icon: StartIcon,
+ rows: [{ title: 'Trigger', value: 'Manual' }],
+ }
+
+ const agent: WorkflowBlockDef = {
+ color: '#802FFF',
+ name: 'Agent',
+ icon: AgentIcon,
+ rows: [{ title: 'Model', value: 'gpt-4o' }],
+ }
+
+ const tool: WorkflowBlockDef = {
+ color: '#3B3B3B',
+ name: toolName,
+ icon: template.icon,
+ rows: [{ title: 'Action', value: 'Run' }],
+ }
+
+ if (hasAgent) return [starter, agent, tool]
+ return [starter, tool]
+}
+
+const BLOCK_W = 76
+const EDGE_W = 14
+
+function PreviewWorkflow({ template }: { template: (typeof TEMPLATES)[number] }) {
+ const blocks = buildWorkflowBlocks(template)
+ const goesUp = template.title.charCodeAt(0) % 2 === 0
+
+ const twoBlock = blocks.length === 2
+ const offsets = twoBlock
+ ? goesUp
+ ? [-10, 10]
+ : [10, -10]
+ : goesUp
+ ? [-12, 12, -12]
+ : [12, -12, 12]
+
+ const totalW = blocks.length * BLOCK_W + (blocks.length - 1) * EDGE_W
+
+ return (
+
+
+
+ {blocks.slice(1).map((_, i) => {
+ const x1 = i * (BLOCK_W + EDGE_W) + BLOCK_W
+ const y1 = 35 + offsets[i]
+ const x2 = (i + 1) * (BLOCK_W + EDGE_W)
+ const y2 = 35 + offsets[i + 1]
+ const midX = (x1 + x2) / 2
+ return (
+
+ )
+ })}
+
+
+ {blocks.map((block, i) => {
+ const x = i * (BLOCK_W + EDGE_W)
+ const yCenter = 35 + offsets[i]
+ return (
+
+
+
+ )
+ })}
+
+
+ )
+}
+
+function TemplatePreview({
+ modules,
+ template,
+}: {
+ modules: ModuleTag[]
+ template: (typeof TEMPLATES)[number]
+}) {
+ if (modules.includes('tables')) return
+ if (modules.includes('knowledge-base')) return
+ if (modules.includes('files')) return
+ return
}
interface TemplatePromptsProps {
@@ -58,52 +318,28 @@ interface TemplatePromptsProps {
}
export function TemplatePrompts({ onSelect }: TemplatePromptsProps) {
- const [expanded, setExpanded] = useState(false)
-
return (
-
- {/* Featured grid */}
-
+
+
{FEATURED_TEMPLATES.map((template) => (
))}
- {/* Expand / collapse */}
-
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
- >
- ) : (
- <>
- More examples
- >
- )}
-
-
- {/* Categorized extras */}
- {expanded && (
-
- {GROUPED_EXTRAS.map((group) => (
-
-
- {group.label}
-
-
- {group.templates.map((template) => (
-
- ))}
-
-
- ))}
+ {GROUPED_EXTRAS.map((group) => (
+
+
{group.label}
+
+ {group.templates.map((template) => (
+
+ ))}
+
- )}
+ ))}
)
}
@@ -113,7 +349,7 @@ interface TemplateCardProps {
onSelect: (prompt: string) => void
}
-function TemplateCard({ template, onSelect }: TemplateCardProps) {
+const TemplateCard = memo(function TemplateCard({ template, onSelect }: TemplateCardProps) {
const Icon = template.icon
return (
@@ -123,7 +359,7 @@ function TemplateCard({ template, onSelect }: TemplateCardProps) {
aria-label={`Select template: ${template.title}`}
className='group flex cursor-pointer flex-col text-left'
>
-
+
{template.image ? (
) : (
-
-
-
+
)}
-
-
-
- {template.title}
-
-
+
+
+ {template.title}
)
-}
+})
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx
index 2c36371c453..1c106b90642 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx
@@ -84,7 +84,7 @@ import { useAnimatedPlaceholder } from '../../hooks'
const TEXTAREA_BASE_CLASSES = cn(
'm-0 box-border h-auto min-h-[24px] w-full resize-none',
- 'overflow-y-auto overflow-x-hidden break-words border-0 bg-transparent',
+ 'overflow-y-auto overflow-x-hidden break-all border-0 bg-transparent',
'px-[4px] py-[4px] font-body text-[15px] leading-[24px] tracking-[-0.015em]',
'text-transparent caret-[var(--text-primary)] outline-none',
'placeholder:font-[380] placeholder:text-[var(--text-subtle)]',
@@ -94,7 +94,7 @@ const TEXTAREA_BASE_CLASSES = cn(
const OVERLAY_CLASSES = cn(
'pointer-events-none absolute top-0 left-0 m-0 box-border h-auto w-full resize-none',
- 'overflow-y-auto overflow-x-hidden whitespace-pre-wrap break-words border-0 bg-transparent',
+ 'overflow-y-auto overflow-x-hidden whitespace-pre-wrap break-all border-0 bg-transparent',
'px-[4px] py-[4px] font-body text-[15px] leading-[24px] tracking-[-0.015em]',
'text-[var(--text-primary)] outline-none',
'[-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden'
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx
index 53e5a36e51b..b9a679bb26b 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx
@@ -6,7 +6,7 @@ import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/type
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
const USER_MESSAGE_CLASSES =
- 'whitespace-pre-wrap font-[430] font-[family-name:var(--font-inter)] text-[15px] text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'
+ 'whitespace-pre-wrap break-all font-[430] font-[family-name:var(--font-inter)] text-[15px] text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'
interface UserMessageContentProps {
content: string
diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx
index 78097c32a6e..8bc0dac39b2 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx
@@ -3,7 +3,6 @@
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useParams, useRouter } from 'next/navigation'
-import { Skeleton } from '@/components/emcn'
import { PanelLeft } from '@/components/emcn/icons'
import { getDocumentIcon } from '@/components/icons/document-icons'
import { useSession } from '@/lib/auth/auth-client'
@@ -46,23 +45,6 @@ function FileAttachmentPill({ mediaType, filename }: FileAttachmentPillProps) {
)
}
-const SKELETON_LINE_COUNT = 4
-
-function ChatSkeleton({ children }: { children: React.ReactNode }) {
- return (
-
-
-
- {Array.from({ length: SKELETON_LINE_COUNT }).map((_, i) => (
-
- ))}
-
-
-
{children}
-
- )
-}
-
interface HomeProps {
chatId?: string
}
@@ -77,6 +59,8 @@ export function Home({ chatId }: HomeProps = {}) {
const templateRef = useRef
(null)
const baseInputHeightRef = useRef(null)
+ const [isInputEntering, setIsInputEntering] = useState(false)
+
const createWorkflowFromLandingSeed = useCallback(
async (seed: LandingWorkflowSeed) => {
try {
@@ -150,7 +134,7 @@ export function Home({ chatId }: HomeProps = {}) {
const wasSendingRef = useRef(false)
- const { isLoading: isLoadingHistory } = useChatHistory(chatId)
+ useChatHistory(chatId)
const { mutate: markRead } = useMarkTaskRead(workspaceId)
const [isResourceCollapsed, setIsResourceCollapsed] = useState(true)
@@ -177,7 +161,6 @@ export function Home({ chatId }: HomeProps = {}) {
const {
messages,
isSending,
- isReconnecting,
sendMessage,
stopGeneration,
resolvedChatId,
@@ -245,6 +228,11 @@ export function Home({ chatId }: HomeProps = {}) {
(text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => {
const trimmed = text.trim()
if (!trimmed && !(fileAttachments && fileAttachments.length > 0)) return
+
+ if (initialViewInputRef.current) {
+ setIsInputEntering(true)
+ }
+
sendMessage(trimmed || 'Analyze the attached file(s).', fileAttachments, contexts)
},
[sendMessage]
@@ -339,22 +327,7 @@ export function Home({ chatId }: HomeProps = {}) {
return () => ro.disconnect()
}, [hasMessages])
- if (chatId && (isLoadingHistory || isReconnecting)) {
- return (
-
-
-
- )
- }
-
- if (!hasMessages) {
+ if (!hasMessages && !chatId) {
return (
@@ -373,7 +346,10 @@ export function Home({ chatId }: HomeProps = {}) {
/>
-
@@ -418,7 +394,7 @@ export function Home({ chatId }: HomeProps = {}) {
})}
)}
-
@@ -451,7 +427,10 @@ export function Home({ chatId }: HomeProps = {}) {
-
+
setIsInputEntering(false) : undefined}
+ >
{
const last = blocks[blocks.length - 1]
- if (last?.type === 'text') return last
+ if (last?.type === 'text' && last.subagent === activeSubagent) return last
const b: ContentBlock = { type: 'text', content: '' }
blocks.push(b)
return b
@@ -561,7 +561,6 @@ export function useChat(
}
logger.debug('SSE event received', parsed)
-
switch (parsed.type) {
case 'chat_id': {
if (parsed.chatId) {
@@ -610,6 +609,7 @@ export function useChat(
const tb = ensureTextBlock()
const normalizedChunk = needsBoundaryNewline ? `\n${chunk}` : chunk
tb.content = (tb.content ?? '') + normalizedChunk
+ if (activeSubagent) tb.subagent = activeSubagent
runningText += normalizedChunk
lastContentSource = contentSource
streamingContentRef.current = runningText
@@ -834,6 +834,7 @@ export function useChat(
}
case 'subagent_end': {
activeSubagent = undefined
+ blocks.push({ type: 'subagent_end' })
flush()
break
}
diff --git a/apps/sim/app/workspace/[workspaceId]/home/types.ts b/apps/sim/app/workspace/[workspaceId]/home/types.ts
index 0f34f0daa2e..1ed8d0ac65e 100644
--- a/apps/sim/app/workspace/[workspaceId]/home/types.ts
+++ b/apps/sim/app/workspace/[workspaceId]/home/types.ts
@@ -129,11 +129,18 @@ export type ToolPhase =
export type ToolCallStatus = 'executing' | 'success' | 'error' | 'cancelled'
+export interface ToolCallResult {
+ success: boolean
+ output?: unknown
+ error?: string
+}
+
export interface ToolCallData {
id: string
toolName: string
displayTitle: string
status: ToolCallStatus
+ result?: ToolCallResult
}
export interface ToolCallInfo {
@@ -155,6 +162,7 @@ export type ContentBlockType =
| 'text'
| 'tool_call'
| 'subagent'
+ | 'subagent_end'
| 'subagent_text'
| 'options'
| 'stopped'
@@ -162,6 +170,7 @@ export type ContentBlockType =
export interface ContentBlock {
type: ContentBlockType
content?: string
+ subagent?: string
toolCall?: ToolCallInfo
options?: OptionItem[]
}
diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx
index 5adce99b22c..f0dea2817f9 100644
--- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/create-schedule-modal/schedule-modal.tsx
@@ -486,7 +486,7 @@ export function ScheduleModal({ open, onOpenChange, workspaceId, schedule }: Sch
onValueChange={(value) => setLifecycle(value as 'persistent' | 'until_complete')}
>
Recurring
- Until Complete
+ Number of runs
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx
index 349f0fb84a0..463d5a415b4 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx
@@ -243,11 +243,9 @@ export function SettingsSidebar({
return (
- {!isCollapsed && (
-
- )}
+
{sectionItems.map((item) => {
const Icon = item.icon
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx
index 04bfafe163a..c932493470e 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx
@@ -495,7 +495,7 @@ export function WorkspaceHeader({
-
+
- {!isCollapsed && (
-
- )}
+
{workspaceNavItems.map((item) => (
{/* Tasks */}
+
+
All tasks
+ {!isCollapsed && (
+
+
+
+ router.push(`/workspace/${workspaceId}/home`)}
+ >
+
+
+
+
+ New task
+
+
+
+ )}
+
{isCollapsed ? (
router.push(`/workspace/${workspaceId}/home`)}
ariaLabel='Tasks'
+ className='mt-[6px]'
>
{tasksLoading ? (
@@ -1140,211 +1160,183 @@ export const Sidebar = memo(function Sidebar() {
)}
) : (
-
-
-
-
- All tasks
-
-
-
-
- router.push(`/workspace/${workspaceId}/home`)}
- >
-
-
-
-
- New task
-
-
-
-
-
-
- {tasksLoading ? (
-
- ) : (
- <>
- {tasks.slice(0, visibleTaskCount).map((task) => {
- const isCurrentRoute = task.id !== 'new' && pathname === task.href
- const isRenaming = renamingTaskId === task.id
- const isSelected = task.id !== 'new' && selectedTasks.has(task.id)
-
- if (isRenaming) {
- return (
-
-
- setRenameValue(e.target.value)}
- onKeyDown={handleRenameKeyDown}
- onBlur={handleSaveTaskRename}
- className='min-w-0 flex-1 border-none bg-transparent font-base text-[14px] text-[var(--text-body)] outline-none'
- />
-
- )
- }
+
+ {tasksLoading ? (
+
+ ) : (
+ <>
+ {tasks.slice(0, visibleTaskCount).map((task) => {
+ const isCurrentRoute = task.id !== 'new' && pathname === task.href
+ const isRenaming = renamingTaskId === task.id
+ const isSelected = task.id !== 'new' && selectedTasks.has(task.id)
+ if (isRenaming) {
return (
-
+ className='mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] bg-[var(--surface-active)] px-[8px] text-[14px]'
+ >
+
+ setRenameValue(e.target.value)}
+ onKeyDown={handleRenameKeyDown}
+ onBlur={handleSaveTaskRename}
+ className='min-w-0 flex-1 border-none bg-transparent font-base text-[14px] text-[var(--text-body)] outline-none'
+ />
+
)
- })}
- {tasks.length > visibleTaskCount && (
-
setVisibleTaskCount((prev) => prev + 5)}
- className='mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] text-[var(--text-icon)] hover:bg-[var(--surface-active)]'
- >
-
- See more
-
- )}
- >
- )}
-
+ }
+
+ return (
+
+ )
+ })}
+ {tasks.length > visibleTaskCount && (
+
setVisibleTaskCount((prev) => prev + 5)}
+ className='mx-[2px] flex h-[30px] items-center gap-[8px] rounded-[8px] px-[8px] text-[14px] text-[var(--text-icon)] hover:bg-[var(--surface-active)]'
+ >
+
+ See more
+
+ )}
+ >
+ )}
)}
{/* Workflows */}
- {isCollapsed ? (
-
- }
- hover={workflowsHover}
- onClick={handleCreateWorkflow}
- ariaLabel='Workflows'
- className='mt-[14px]'
- >
- {workflowsLoading && regularWorkflows.length === 0 ? (
-
-
- Loading...
-
- ) : regularWorkflows.length === 0 ? (
- No workflows yet
- ) : (
- <>
-
- {(workflowsByFolder.root || []).map((workflow) => (
-
-
-
- {workflow.name}
-
-
- ))}
- >
- )}
-
- ) : (
-
-
-
-
- Workflows
-
-
-
-
-
-
-
- {isImporting || isCreatingFolder ? (
-
- ) : (
-
- )}
-
-
-
-
- More actions
-
-
-
-
-
- {isImporting ? 'Importing...' : 'Import workflow'}
-
-
-
- {isCreatingFolder ? 'Creating folder...' : 'Create folder'}
-
-
-
+
+
+
Workflows
+ {!isCollapsed && (
+
+
-
-
-
+
+
+ {isImporting || isCreatingFolder ? (
+
+ ) : (
+
+ )}
+
+
- {isCreatingWorkflow ? 'Creating workflow...' : 'New workflow'}
+ More actions
-
+
+
+
+ {isImporting ? 'Importing...' : 'Import workflow'}
+
+
+
+ {isCreatingFolder ? 'Creating folder...' : 'Create folder'}
+
+
+
+
+
+
+
+
+
+
+ {isCreatingWorkflow ? 'Creating workflow...' : 'New workflow'}
+
+
-
-
+ )}
+
+ {isCollapsed ? (
+
+ }
+ hover={workflowsHover}
+ onClick={handleCreateWorkflow}
+ ariaLabel='Workflows'
+ className='mt-[6px]'
+ >
+ {workflowsLoading && regularWorkflows.length === 0 ? (
+
+
+ Loading...
+
+ ) : regularWorkflows.length === 0 ? (
+
No workflows yet
+ ) : (
+ <>
+
+ {(workflowsByFolder.root || []).map((workflow) => (
+
+
+
+ {workflow.name}
+
+
+ ))}
+ >
+ )}
+
+ ) : (
{workflowsLoading && regularWorkflows.length === 0 && }
-
- )}
+ )}
+
{/* Footer */}
diff --git a/apps/sim/components/emcn/icons/animate/pills-ring.module.css b/apps/sim/components/emcn/icons/animate/pills-ring.module.css
new file mode 100644
index 00000000000..20e46e6fd4b
--- /dev/null
+++ b/apps/sim/components/emcn/icons/animate/pills-ring.module.css
@@ -0,0 +1,22 @@
+/**
+ * PillsRing icon animation
+ * Pills arranged in a ring fade in/out sequentially,
+ * creating a chasing spinner effect.
+ * Individual pill delays are set via inline style.
+ */
+
+@keyframes pill-fade {
+ 0%,
+ 50%,
+ 100% {
+ opacity: 0.15;
+ }
+ 25% {
+ opacity: 1;
+ }
+}
+
+.animated-pills-ring-svg .pill {
+ animation: pill-fade 1.2s ease-in-out infinite;
+ will-change: opacity;
+}
diff --git a/apps/sim/components/emcn/icons/index.ts b/apps/sim/components/emcn/icons/index.ts
index 2bf3a993170..21c3fb3204d 100644
--- a/apps/sim/components/emcn/icons/index.ts
+++ b/apps/sim/components/emcn/icons/index.ts
@@ -53,6 +53,7 @@ export { Palette } from './palette'
export { PanelLeft } from './panel-left'
export { Pause } from './pause'
export { Pencil } from './pencil'
+export { PillsRing } from './pills-ring'
export { Play, PlayOutline } from './play'
export { Plus } from './plus'
export { Redo } from './redo'
diff --git a/apps/sim/components/emcn/icons/pills-ring.tsx b/apps/sim/components/emcn/icons/pills-ring.tsx
new file mode 100644
index 00000000000..94167283fc9
--- /dev/null
+++ b/apps/sim/components/emcn/icons/pills-ring.tsx
@@ -0,0 +1,52 @@
+import type { SVGProps } from 'react'
+import styles from '@/components/emcn/icons/animate/pills-ring.module.css'
+
+export interface PillsRingProps extends SVGProps {
+ /**
+ * Enable the chasing fade animation
+ * @default false
+ */
+ animate?: boolean
+}
+
+const PILL_COUNT = 8
+const DURATION_S = 1.2
+
+/**
+ * Ring of pill-shaped elements with optional chasing fade animation.
+ * Static render shows pills at graded opacities; animated render
+ * fades them sequentially around the ring via CSS module keyframes.
+ * @param props - SVG properties including className, animate, etc.
+ */
+export function PillsRing({ animate = false, className, ...props }: PillsRingProps) {
+ const svgClassName = animate
+ ? `${styles['animated-pills-ring-svg']} ${className || ''}`.trim()
+ : className
+
+ return (
+
+ {Array.from({ length: PILL_COUNT }).map((_, i) => (
+
+ ))}
+
+ )
+}
diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx
index b098e616a2f..a1c6beb37fd 100644
--- a/apps/sim/components/icons.tsx
+++ b/apps/sim/components/icons.tsx
@@ -1390,7 +1390,7 @@ export function AmplitudeIcon(props: SVGProps) {
return (
diff --git a/apps/sim/content/authors/adam.json b/apps/sim/content/authors/adam.json
deleted file mode 100644
index 3f089f7906d..00000000000
--- a/apps/sim/content/authors/adam.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "id": "adam",
- "name": "Adam Gough",
- "url": "https://x.com/adamgough",
- "xHandle": "adamgough",
- "avatarUrl": "/studio/authors/adam.png"
-}
diff --git a/apps/sim/content/authors/emir.json b/apps/sim/content/authors/emir.json
index f317287e23a..eb0e5305d25 100644
--- a/apps/sim/content/authors/emir.json
+++ b/apps/sim/content/authors/emir.json
@@ -3,5 +3,5 @@
"name": "Emir Karabeg",
"url": "https://x.com/karabegemir",
"xHandle": "karabegemir",
- "avatarUrl": "/studio/authors/emir.jpg"
+ "avatarUrl": "/blog/authors/emir.jpg"
}
diff --git a/apps/sim/content/authors/sid.json b/apps/sim/content/authors/sid.json
index 1703337f924..e799abd0151 100644
--- a/apps/sim/content/authors/sid.json
+++ b/apps/sim/content/authors/sid.json
@@ -3,5 +3,5 @@
"name": "Siddharth",
"url": "https://x.com/sidganesan",
"xHandle": "sidganesan",
- "avatarUrl": "/studio/authors/sid.jpg"
+ "avatarUrl": "/blog/authors/sid.jpg"
}
diff --git a/apps/sim/content/authors/vik.json b/apps/sim/content/authors/vik.json
index 859e491c36e..5b514fd51a2 100644
--- a/apps/sim/content/authors/vik.json
+++ b/apps/sim/content/authors/vik.json
@@ -3,5 +3,5 @@
"name": "Vikhyath Mondreti",
"url": "https://github.com/icecrasher321",
"xHandle": "icecrasher321",
- "avatarUrl": "/studio/authors/vik.jpg"
+ "avatarUrl": "/blog/authors/vik.jpg"
}
diff --git a/apps/sim/content/authors/waleed.json b/apps/sim/content/authors/waleed.json
index b6cf7c8a57c..4fe2ca33554 100644
--- a/apps/sim/content/authors/waleed.json
+++ b/apps/sim/content/authors/waleed.json
@@ -3,5 +3,5 @@
"name": "Waleed Latif",
"url": "https://x.com/typingwala",
"xHandle": "typingwala",
- "avatarUrl": "/studio/authors/waleed.jpg"
+ "avatarUrl": "/blog/authors/waleed.jpg"
}
diff --git a/apps/sim/content/blog/copilot/index.mdx b/apps/sim/content/blog/copilot/index.mdx
index b68ceb5f6e7..98add964847 100644
--- a/apps/sim/content/blog/copilot/index.mdx
+++ b/apps/sim/content/blog/copilot/index.mdx
@@ -8,11 +8,11 @@ authors:
- sid
readingTime: 7
tags: [Copilot, AI Assistant, Benchmarks, Architecture, Sim]
-ogImage: /studio/copilot/cover.png
+ogImage: /blog/copilot/cover.png
ogAlt: 'Sim Copilot technical overview'
about: ['AI Assistants', 'Agentic Workflows', 'Retrieval Augmented Generation']
timeRequired: PT7M
-canonical: https://sim.ai/studio/copilot
+canonical: https://sim.ai/blog/copilot
featured: false
draft: true
---
diff --git a/apps/sim/content/blog/emcn/index.mdx b/apps/sim/content/blog/emcn/index.mdx
index f03a855bd9c..9dddba8244f 100644
--- a/apps/sim/content/blog/emcn/index.mdx
+++ b/apps/sim/content/blog/emcn/index.mdx
@@ -8,18 +8,18 @@ authors:
- emir
readingTime: 6
tags: [Design, Emcn, UI, UX, Components, Sim]
-ogImage: /studio/emcn/cover.png
+ogImage: /blog/emcn/cover.png
ogAlt: 'Emcn design system cover'
about: ['Design Systems', 'Component Libraries', 'Design Tokens', 'Accessibility']
timeRequired: PT6M
-canonical: https://sim.ai/studio/emcn
+canonical: https://sim.ai/blog/emcn
featured: false
draft: true
---
> This post is the scaffolding for Emcn, our new design system. We’ll fill it in as we publish the full documentation and component gallery.
-
+
## What is Emcn?
diff --git a/apps/sim/content/blog/enterprise/index.mdx b/apps/sim/content/blog/enterprise/index.mdx
index 2a04d8af432..f97fbeef10e 100644
--- a/apps/sim/content/blog/enterprise/index.mdx
+++ b/apps/sim/content/blog/enterprise/index.mdx
@@ -8,11 +8,11 @@ authors:
- vik
readingTime: 10
tags: [Enterprise, Security, Self-Hosted, SSO, SAML, Compliance, BYOK, Access Control, Copilot, Whitelabel, API, Import, Export]
-ogImage: /studio/enterprise/cover.png
+ogImage: /blog/enterprise/cover.png
ogAlt: 'Sim Enterprise features overview'
about: ['Enterprise Software', 'Security', 'Compliance', 'Self-Hosting']
timeRequired: PT10M
-canonical: https://sim.ai/studio/enterprise
+canonical: https://sim.ai/blog/enterprise
featured: true
draft: false
---
@@ -21,13 +21,13 @@ We've been working with security teams at larger organizations to bring Sim into
## Access Control
-
+
Permission groups let administrators control what features and integrations are available to different teams within an organization. This isn't just UI filtering—restrictions are enforced at the execution layer.
### Model Provider Restrictions
-
+
Allowlist specific providers while blocking others. Users in a restricted group see only approved providers in the model selector. A workflow that tries to use an unapproved provider won't execute.
@@ -35,13 +35,13 @@ This is useful when you've approved certain providers for production use, negoti
### Integration Controls
-
+
Restrict which workflow blocks appear in the editor. Disable the HTTP block to prevent arbitrary external API calls. Block access to integrations that haven't completed your security review.
### Platform Feature Toggles
-
+
Control access to platform capabilities per permission group:
@@ -56,7 +56,7 @@ Users not assigned to any permission group have full access, so restrictions are
## Bring Your Own Keys
-
+
When you configure your own API keys for model providers—OpenAI, Anthropic, Google, Azure OpenAI, AWS Bedrock, or any supported provider—your prompts and completions route directly between Sim and that provider. The traffic doesn't pass through our infrastructure.
@@ -70,7 +70,7 @@ A healthcare organization can use Azure OpenAI with their BAA-covered subscripti
## Self-Hosted Deployments
-
+
Run Sim entirely on your infrastructure. Deploy with [Docker Compose](https://docs.sim.ai/self-hosting/docker) or [Helm charts](https://docs.sim.ai/self-hosting/kubernetes) for Kubernetes—the application, WebSocket server, and PostgreSQL database all stay within your network.
@@ -96,7 +96,7 @@ This is particularly relevant for organizations where the context Copilot needs
## SSO & SAML
-
+
Integrate with your existing identity provider through SAML 2.0 or OIDC. We support Okta, Azure AD (Entra ID), Google Workspace, OneLogin, Auth0, JumpCloud, Ping Identity, ADFS, and any compliant identity provider.
@@ -126,7 +126,7 @@ This is useful for internal platforms, customer-facing deployments, or any scena
## Compliance & Data Retention
-
+
Sim maintains **SOC 2 Type II** certification with annual audits covering security, availability, and confidentiality controls. We share our SOC 2 report directly with prospective customers under NDA.
diff --git a/apps/sim/content/blog/executor/index.mdx b/apps/sim/content/blog/executor/index.mdx
index 21cbe5b4a7d..61c9407ee44 100644
--- a/apps/sim/content/blog/executor/index.mdx
+++ b/apps/sim/content/blog/executor/index.mdx
@@ -8,11 +8,11 @@ authors:
- sid
readingTime: 12
tags: [Executor, Architecture, DAG, Orchestration]
-ogImage: /studio/executor/cover.png
+ogImage: /blog/executor/cover.png
ogAlt: 'Sim Executor technical overview'
about: ['Execution', 'Workflow Orchestration']
timeRequired: PT12M
-canonical: https://sim.ai/studio/executor
+canonical: https://sim.ai/blog/executor
featured: false
draft: false
---
@@ -106,7 +106,7 @@ Behind the scenes, this expands to the same branch‑indexed DAG structure. The
Loops present a challenge for DAGs: graphs are acyclic, but loops repeat. We handle this by expanding loops into sentinel nodes during compilation.
-
+
*Loops expand into sentinel start and end nodes. The backward edge only activates when the loop continues, preserving the DAG's acyclic property.*
A loop is bookended by two nodes: a sentinel start and a sentinel end. The sentinel start activates the first blocks inside the loop. When terminal blocks complete, they route to the sentinel end. The sentinel end evaluates the loop condition and returns either "continue" (which routes back to the start) or "exit" (which activates blocks after the loop).
@@ -135,7 +135,7 @@ This makes AI-driven routing deterministic and traceable. You can inspect the ex
When a condition or router executes, it evaluates its logic and returns a single selection. The edge manager checks each outgoing edge to see if its label matches the selection. The matching edge activates; the rest deactivate.
-
+
*When a condition selects one path, the chosen edge activates while unselected paths deactivate recursively, preventing unreachable blocks from executing.*
Deactivation cascades. If an edge deactivates, the executor recursively deactivates all edges downstream from its target—unless that target has other active incoming edges. This automatic pruning prevents unreachable blocks from ever entering the ready queue.
@@ -182,7 +182,7 @@ The lock also prevents a resumed node from racing with other executing nodes. Wh
### Example
-
+
*A common pattern: agent generates output, pauses for human review, router decides pass/fail based on feedback, saves to workflow variable, and loop continues until approved.*
A while loop runs an agent with previous feedback as context. The agent's output goes to a human‑in‑the‑loop block, which pauses execution and sends a notification. The user reviews the output and provides feedback via the resume link.
diff --git a/apps/sim/content/blog/multiplayer/index.mdx b/apps/sim/content/blog/multiplayer/index.mdx
index f0dd28826aa..58b59970815 100644
--- a/apps/sim/content/blog/multiplayer/index.mdx
+++ b/apps/sim/content/blog/multiplayer/index.mdx
@@ -8,8 +8,8 @@ authors:
- vik
readingTime: 12
tags: [Multiplayer, Realtime, Collaboration, WebSockets, Architecture]
-ogImage: /studio/multiplayer/cover.png
-canonical: https://sim.ai/studio/multiplayer
+ogImage: /blog/multiplayer/cover.png
+canonical: https://sim.ai/blog/multiplayer
draft: false
---
diff --git a/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx b/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx
index c57f73d01fc..f570590bd5e 100644
--- a/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx
+++ b/apps/sim/content/blog/openai-vs-n8n-vs-sim/index.mdx
@@ -8,8 +8,8 @@ authors:
- emir
readingTime: 9
tags: [AI Agents, Workflow Automation, OpenAI AgentKit, n8n, Sim, MCP]
-ogImage: /studio/openai-vs-n8n-vs-sim/workflow.png
-canonical: https://sim.ai/studio/openai-vs-n8n-vs-sim
+ogImage: /blog/openai-vs-n8n-vs-sim/workflow.png
+canonical: https://sim.ai/blog/openai-vs-n8n-vs-sim
draft: false
---
@@ -19,7 +19,7 @@ When building AI agent workflows, developers often evaluate multiple platforms t
OpenAI AgentKit is a set of building blocks designed to help developers take AI agents from prototype to production. Built on top of the OpenAI Responses API, it provides a structured approach to building and deploying intelligent agents.
-
+
### Core Features
@@ -31,7 +31,7 @@ AgentKit provides a visual canvas where developers can design and build agents.
ChatKit enables developers to embed chat interfaces to run workflows directly within their applications. It includes custom widgets that you can create and integrate, with the ability to preview interfaces right in the workflow builder before deployment.
-
+
#### Comprehensive Evaluation System
@@ -65,7 +65,7 @@ While AgentKit is powerful for building agents, it has some limitations:
n8n is a workflow automation platform that excels at connecting various services and APIs together. While it started as a general automation tool, n8n has evolved to support AI agent workflows alongside its traditional integration capabilities.
-
+
### Core Capabilities
@@ -117,19 +117,19 @@ Sim is a fully open-source platform (Apache 2.0 license) specifically designed f
Sim provides an intuitive drag-and-drop canvas where developers can build complex AI agent workflows visually. The platform supports sophisticated agent architectures, including multi-agent systems, conditional logic, loops, and parallel execution paths. Additionally, Sim's built-in AI Copilot can assist you directly in the editor, helping you build and modify workflows faster with intelligent suggestions and explanations.
-
+
#### AI Copilot for Workflow Building
Sim includes an intelligent in-editor AI assistant that helps you build and edit workflows faster. Copilot can explain complex concepts, suggest best practices, and even make changes to your workflow when you approve them. Using the @ context menu, you can reference workflows, blocks, knowledge bases, documentation, templates, and execution logs—giving Copilot the full context it needs to provide accurate, relevant assistance. This dramatically accelerates workflow development compared to building from scratch.
-
+
#### Pre-Built Workflow Templates
Get started quickly with Sim's extensive library of pre-built workflow templates. Browse templates across categories like Marketing, Sales, Finance, Support, and Artificial Intelligence. Each template is a production-ready workflow you can customize for your needs, saving hours of development time. Templates are created by the Sim team and community members, with popularity ratings and integration counts to help you find the right starting point.
-
+
#### 80+ Built-in Integrations
@@ -155,7 +155,7 @@ Sim's native knowledge base goes far beyond simple document storage. Powered by
Sim provides enterprise-grade logging that captures every detail of workflow execution. Track workflow runs with execution IDs, view block-level logs with precise timing and duration metrics, monitor token usage and costs per execution, and debug failures with detailed error traces and trace spans. The logging system integrates with Copilot—you can reference execution logs directly in your Copilot conversations to understand what happened and troubleshoot issues. This level of observability is essential for production AI agents where understanding behavior and debugging issues quickly is critical.
-
+
#### Custom Integrations via MCP Protocol
diff --git a/apps/sim/content/blog/series-a/index.mdx b/apps/sim/content/blog/series-a/index.mdx
index 2c4c0daa14e..e029c884a28 100644
--- a/apps/sim/content/blog/series-a/index.mdx
+++ b/apps/sim/content/blog/series-a/index.mdx
@@ -9,16 +9,16 @@ authors:
- emir
readingTime: 4
tags: [Announcement, Funding, Series A, Sim, YCombinator]
-ogImage: /studio/series-a/cover.png
+ogImage: /blog/series-a/cover.png
ogAlt: 'Sim team photo in front of neon logo'
about: ['Artificial Intelligence', 'Agentic Workflows', 'Startups', 'Funding']
timeRequired: PT4M
-canonical: https://sim.ai/studio/series-a
+canonical: https://sim.ai/blog/series-a
featured: true
draft: false
---
-
+
## Why we’re excited
diff --git a/apps/sim/content/blog/v0-5/index.mdx b/apps/sim/content/blog/v0-5/index.mdx
index 9d3bd131f97..b97609f41c7 100644
--- a/apps/sim/content/blog/v0-5/index.mdx
+++ b/apps/sim/content/blog/v0-5/index.mdx
@@ -8,11 +8,11 @@ authors:
- waleed
readingTime: 8
tags: [Release, Copilot, MCP, Observability, Collaboration, Integrations, Sim]
-ogImage: /studio/v0-5/cover.png
+ogImage: /blog/v0-5/cover.png
ogAlt: 'Sim v0.5 release announcement'
about: ['AI Agents', 'Workflow Automation', 'Developer Tools']
timeRequired: PT8M
-canonical: https://sim.ai/studio/v0-5
+canonical: https://sim.ai/blog/v0-5
featured: true
draft: false
---
@@ -21,7 +21,7 @@ draft: false
## Copilot
-
+
Copilot is a context-aware assistant embedded in the Sim editor. Unlike general-purpose AI assistants, Copilot has direct access to your workspace: workflows, block configurations, execution logs, connected credentials, and documentation. It can also search the web to pull in external context when needed.
@@ -45,7 +45,7 @@ For complex tasks, Copilot uses subagents—breaking requests into discrete oper
## MCP Deployment
-
+
Deploy any workflow as an [MCP](https://modelcontextprotocol.io) server. Once deployed, the workflow becomes a callable tool for any MCP-compatible agent—[Claude Desktop](https://claude.ai/download), [Cursor](https://cursor.com), or your own applications.
@@ -57,7 +57,7 @@ This pattern scales to research pipelines, data processing workflows, approval c
## Logs & Dashboard
-
+
Every workflow execution generates a full trace. Each block records its start time, end time, inputs, outputs, and any errors. For LLM blocks, we capture prompt tokens, completion tokens, and cost by model.
@@ -73,7 +73,7 @@ This level of observability is necessary when workflows handle production traffi
## Realtime Collaboration
-
+
Multiple users can edit the same workflow simultaneously. Changes propagate in real time—you see teammates' cursors, block additions, and configuration updates as they happen.
@@ -83,7 +83,7 @@ This is particularly useful during development sessions where engineers, product
## Versioning
-
+
Every deployment creates a new version. The version history shows who deployed what and when, with a preview of the workflow state at that point in time. Roll back to any previous version with one click—the live deployment updates immediately.
@@ -93,7 +93,7 @@ This matters when something breaks in production. You can instantly revert to th
## 100+ Integrations
-
+
v0.5 adds **100+ integrations** with **300+ actions**. These cover the specific operations you need—not just generic CRUD, but actions like "send Slack message to channel," "create Jira ticket with custom fields," "query Postgres with parameterized SQL," or "enrich contact via Apollo."
@@ -130,7 +130,7 @@ Workflows can be triggered through multiple mechanisms:
## Knowledge Base
-
+
Upload documents—PDFs, text files, markdown, HTML—and make them queryable by your agents. This is [RAG](https://en.wikipedia.org/wiki/Retrieval-augmented_generation) (Retrieval Augmented Generation) built directly into Sim.
diff --git a/apps/sim/lib/blog/seo.ts b/apps/sim/lib/blog/seo.ts
index 0d62048d069..ef314d2385b 100644
--- a/apps/sim/lib/blog/seo.ts
+++ b/apps/sim/lib/blog/seo.ts
@@ -108,7 +108,7 @@ export function buildBreadcrumbJsonLd(post: BlogMeta) {
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://sim.ai' },
- { '@type': 'ListItem', position: 2, name: 'Sim Studio', item: 'https://sim.ai/studio' },
+ { '@type': 'ListItem', position: 2, name: 'Blog', item: 'https://sim.ai/blog' },
{ '@type': 'ListItem', position: 3, name: post.title, item: post.canonical },
],
}
@@ -131,8 +131,8 @@ export function buildBlogJsonLd() {
return {
'@context': 'https://schema.org',
'@type': 'Blog',
- name: 'Sim Studio',
- url: 'https://sim.ai/studio',
+ name: 'Sim Blog',
+ url: 'https://sim.ai/blog',
description: 'Announcements, insights, and guides for building AI agent workflows.',
}
}
diff --git a/apps/sim/next.config.ts b/apps/sim/next.config.ts
index f1058f58347..e5975cc6e9a 100644
--- a/apps/sim/next.config.ts
+++ b/apps/sim/next.config.ts
@@ -337,30 +337,30 @@ const nextConfig: NextConfig = {
}
)
- // Redirect /building and /blog to /studio (legacy URL support)
+ // Redirect /building and /studio to /blog (legacy URL support)
redirects.push(
{
source: '/building/:path*',
- destination: 'https://sim.ai/studio/:path*',
+ destination: 'https://sim.ai/blog/:path*',
permanent: true,
},
{
- source: '/blog/:path*',
- destination: 'https://sim.ai/studio/:path*',
+ source: '/studio/:path*',
+ destination: 'https://sim.ai/blog/:path*',
permanent: true,
}
)
- // Move root feeds to studio namespace
+ // Move root feeds to blog namespace
redirects.push(
{
source: '/rss.xml',
- destination: '/studio/rss.xml',
+ destination: '/blog/rss.xml',
permanent: true,
},
{
source: '/sitemap-images.xml',
- destination: '/studio/sitemap-images.xml',
+ destination: '/blog/sitemap-images.xml',
permanent: true,
}
)
diff --git a/apps/sim/public/studio/authors/emir.jpg b/apps/sim/public/blog/authors/emir.jpg
similarity index 100%
rename from apps/sim/public/studio/authors/emir.jpg
rename to apps/sim/public/blog/authors/emir.jpg
diff --git a/apps/sim/public/studio/authors/sid.jpg b/apps/sim/public/blog/authors/sid.jpg
similarity index 100%
rename from apps/sim/public/studio/authors/sid.jpg
rename to apps/sim/public/blog/authors/sid.jpg
diff --git a/apps/sim/public/studio/authors/vik.jpg b/apps/sim/public/blog/authors/vik.jpg
similarity index 100%
rename from apps/sim/public/studio/authors/vik.jpg
rename to apps/sim/public/blog/authors/vik.jpg
diff --git a/apps/sim/public/studio/authors/waleed.jpg b/apps/sim/public/blog/authors/waleed.jpg
similarity index 100%
rename from apps/sim/public/studio/authors/waleed.jpg
rename to apps/sim/public/blog/authors/waleed.jpg
diff --git a/apps/sim/public/studio/copilot/cover.png b/apps/sim/public/blog/copilot/cover.png
similarity index 100%
rename from apps/sim/public/studio/copilot/cover.png
rename to apps/sim/public/blog/copilot/cover.png
diff --git a/apps/sim/public/studio/emcn/cover.png b/apps/sim/public/blog/emcn/cover.png
similarity index 100%
rename from apps/sim/public/studio/emcn/cover.png
rename to apps/sim/public/blog/emcn/cover.png
diff --git a/apps/sim/public/studio/enterprise/access-control.png b/apps/sim/public/blog/enterprise/access-control.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/access-control.png
rename to apps/sim/public/blog/enterprise/access-control.png
diff --git a/apps/sim/public/studio/enterprise/byok.png b/apps/sim/public/blog/enterprise/byok.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/byok.png
rename to apps/sim/public/blog/enterprise/byok.png
diff --git a/apps/sim/public/studio/enterprise/compliance.png b/apps/sim/public/blog/enterprise/compliance.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/compliance.png
rename to apps/sim/public/blog/enterprise/compliance.png
diff --git a/apps/sim/public/studio/enterprise/cover.png b/apps/sim/public/blog/enterprise/cover.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/cover.png
rename to apps/sim/public/blog/enterprise/cover.png
diff --git a/apps/sim/public/studio/enterprise/integration-controls.png b/apps/sim/public/blog/enterprise/integration-controls.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/integration-controls.png
rename to apps/sim/public/blog/enterprise/integration-controls.png
diff --git a/apps/sim/public/studio/enterprise/model-providers.png b/apps/sim/public/blog/enterprise/model-providers.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/model-providers.png
rename to apps/sim/public/blog/enterprise/model-providers.png
diff --git a/apps/sim/public/studio/enterprise/platform-controls.png b/apps/sim/public/blog/enterprise/platform-controls.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/platform-controls.png
rename to apps/sim/public/blog/enterprise/platform-controls.png
diff --git a/apps/sim/public/studio/enterprise/self-hosted.png b/apps/sim/public/blog/enterprise/self-hosted.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/self-hosted.png
rename to apps/sim/public/blog/enterprise/self-hosted.png
diff --git a/apps/sim/public/studio/enterprise/sso.png b/apps/sim/public/blog/enterprise/sso.png
similarity index 100%
rename from apps/sim/public/studio/enterprise/sso.png
rename to apps/sim/public/blog/enterprise/sso.png
diff --git a/apps/sim/public/studio/executor/cover.png b/apps/sim/public/blog/executor/cover.png
similarity index 100%
rename from apps/sim/public/studio/executor/cover.png
rename to apps/sim/public/blog/executor/cover.png
diff --git a/apps/sim/public/studio/executor/edge-pruning.png b/apps/sim/public/blog/executor/edge-pruning.png
similarity index 100%
rename from apps/sim/public/studio/executor/edge-pruning.png
rename to apps/sim/public/blog/executor/edge-pruning.png
diff --git a/apps/sim/public/studio/executor/hitl-loop.png b/apps/sim/public/blog/executor/hitl-loop.png
similarity index 100%
rename from apps/sim/public/studio/executor/hitl-loop.png
rename to apps/sim/public/blog/executor/hitl-loop.png
diff --git a/apps/sim/public/studio/executor/loop-sentinels.png b/apps/sim/public/blog/executor/loop-sentinels.png
similarity index 100%
rename from apps/sim/public/studio/executor/loop-sentinels.png
rename to apps/sim/public/blog/executor/loop-sentinels.png
diff --git a/apps/sim/public/studio/multiplayer/cover.png b/apps/sim/public/blog/multiplayer/cover.png
similarity index 100%
rename from apps/sim/public/studio/multiplayer/cover.png
rename to apps/sim/public/blog/multiplayer/cover.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/copilot.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/copilot.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/copilot.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/copilot.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/logs.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/logs.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/logs.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/logs.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/n8n.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/n8n.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/n8n.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/n8n.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/openai.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/openai.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/openai.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/openai.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/sim.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/sim.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/sim.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/sim.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/templates.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/templates.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/templates.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/templates.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/widgets.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/widgets.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/widgets.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/widgets.png
diff --git a/apps/sim/public/studio/openai-vs-n8n-vs-sim/workflow.png b/apps/sim/public/blog/openai-vs-n8n-vs-sim/workflow.png
similarity index 100%
rename from apps/sim/public/studio/openai-vs-n8n-vs-sim/workflow.png
rename to apps/sim/public/blog/openai-vs-n8n-vs-sim/workflow.png
diff --git a/apps/sim/public/studio/series-a/cover.png b/apps/sim/public/blog/series-a/cover.png
similarity index 100%
rename from apps/sim/public/studio/series-a/cover.png
rename to apps/sim/public/blog/series-a/cover.png
diff --git a/apps/sim/public/studio/series-a/team.jpg b/apps/sim/public/blog/series-a/team.jpg
similarity index 100%
rename from apps/sim/public/studio/series-a/team.jpg
rename to apps/sim/public/blog/series-a/team.jpg
diff --git a/apps/sim/public/blog/thumbnails/copilot.webp b/apps/sim/public/blog/thumbnails/copilot.webp
new file mode 100644
index 00000000000..50c0e934868
Binary files /dev/null and b/apps/sim/public/blog/thumbnails/copilot.webp differ
diff --git a/apps/sim/public/blog/thumbnails/enterprise.webp b/apps/sim/public/blog/thumbnails/enterprise.webp
new file mode 100644
index 00000000000..4e73f5af6b5
Binary files /dev/null and b/apps/sim/public/blog/thumbnails/enterprise.webp differ
diff --git a/apps/sim/public/blog/thumbnails/executor.webp b/apps/sim/public/blog/thumbnails/executor.webp
new file mode 100644
index 00000000000..6b133cdcfde
Binary files /dev/null and b/apps/sim/public/blog/thumbnails/executor.webp differ
diff --git a/apps/sim/public/blog/thumbnails/multiplayer.webp b/apps/sim/public/blog/thumbnails/multiplayer.webp
new file mode 100644
index 00000000000..bf0fb376a00
Binary files /dev/null and b/apps/sim/public/blog/thumbnails/multiplayer.webp differ
diff --git a/apps/sim/public/blog/thumbnails/series-a.webp b/apps/sim/public/blog/thumbnails/series-a.webp
new file mode 100644
index 00000000000..0326140d58c
Binary files /dev/null and b/apps/sim/public/blog/thumbnails/series-a.webp differ
diff --git a/apps/sim/public/blog/thumbnails/v0-5.webp b/apps/sim/public/blog/thumbnails/v0-5.webp
new file mode 100644
index 00000000000..cc971839966
Binary files /dev/null and b/apps/sim/public/blog/thumbnails/v0-5.webp differ
diff --git a/apps/sim/public/studio/v0-5/collaboration.jpg b/apps/sim/public/blog/v0-5/collaboration.jpg
similarity index 100%
rename from apps/sim/public/studio/v0-5/collaboration.jpg
rename to apps/sim/public/blog/v0-5/collaboration.jpg
diff --git a/apps/sim/public/studio/v0-5/collaboration.png b/apps/sim/public/blog/v0-5/collaboration.png
similarity index 100%
rename from apps/sim/public/studio/v0-5/collaboration.png
rename to apps/sim/public/blog/v0-5/collaboration.png
diff --git a/apps/sim/public/studio/v0-5/copilot.jpg b/apps/sim/public/blog/v0-5/copilot.jpg
similarity index 100%
rename from apps/sim/public/studio/v0-5/copilot.jpg
rename to apps/sim/public/blog/v0-5/copilot.jpg
diff --git a/apps/sim/public/studio/v0-5/cover.png b/apps/sim/public/blog/v0-5/cover.png
similarity index 100%
rename from apps/sim/public/studio/v0-5/cover.png
rename to apps/sim/public/blog/v0-5/cover.png
diff --git a/apps/sim/public/studio/v0-5/dashboard.jpg b/apps/sim/public/blog/v0-5/dashboard.jpg
similarity index 100%
rename from apps/sim/public/studio/v0-5/dashboard.jpg
rename to apps/sim/public/blog/v0-5/dashboard.jpg
diff --git a/apps/sim/public/studio/v0-5/integrations.png b/apps/sim/public/blog/v0-5/integrations.png
similarity index 100%
rename from apps/sim/public/studio/v0-5/integrations.png
rename to apps/sim/public/blog/v0-5/integrations.png
diff --git a/apps/sim/public/studio/v0-5/kb.png b/apps/sim/public/blog/v0-5/kb.png
similarity index 100%
rename from apps/sim/public/studio/v0-5/kb.png
rename to apps/sim/public/blog/v0-5/kb.png
diff --git a/apps/sim/public/studio/v0-5/mcp.png b/apps/sim/public/blog/v0-5/mcp.png
similarity index 100%
rename from apps/sim/public/studio/v0-5/mcp.png
rename to apps/sim/public/blog/v0-5/mcp.png
diff --git a/apps/sim/public/studio/v0-5/versioning.png b/apps/sim/public/blog/v0-5/versioning.png
similarity index 100%
rename from apps/sim/public/studio/v0-5/versioning.png
rename to apps/sim/public/blog/v0-5/versioning.png
diff --git a/apps/sim/public/landing/docs-getting-started.svg b/apps/sim/public/landing/docs-getting-started.svg
new file mode 100644
index 00000000000..e34beec1f3b
--- /dev/null
+++ b/apps/sim/public/landing/docs-getting-started.svg
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/sim/public/landing/docs-intro.svg b/apps/sim/public/landing/docs-intro.svg
new file mode 100644
index 00000000000..4a4e8cec2dd
--- /dev/null
+++ b/apps/sim/public/landing/docs-intro.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/sim/tailwind.config.ts b/apps/sim/tailwind.config.ts
index 6ca5e4ab957..f12f246837f 100644
--- a/apps/sim/tailwind.config.ts
+++ b/apps/sim/tailwind.config.ts
@@ -176,6 +176,10 @@ export default {
from: { transform: 'translateX(40px)' },
to: { transform: 'translateX(0)' },
},
+ 'slide-in-bottom': {
+ from: { opacity: '0', transform: 'translateY(20px)' },
+ to: { opacity: '1', transform: 'translateY(0)' },
+ },
},
animation: {
'caret-blink': 'caret-blink 1.25s ease-out infinite',
@@ -185,8 +189,10 @@ export default {
'placeholder-pulse': 'placeholder-pulse 1.5s ease-in-out infinite',
'ring-pulse': 'ring-pulse 1.5s ease-in-out infinite',
'stream-fade-in': 'stream-fade-in 300ms ease-out forwards',
+ 'stream-fade-in-delayed': 'stream-fade-in 300ms ease-out 1.5s forwards',
'thinking-block': 'thinking-block 1.6s ease-in-out infinite',
'slide-in-right': 'slide-in-right 350ms ease-out forwards',
+ 'slide-in-bottom': 'slide-in-bottom 400ms cubic-bezier(0.16, 1, 0.3, 1)',
},
},
},