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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="apps/sim/public/static/sim.png" alt="Sim Studio Logo" width="500"/>
<img src="https://nwkhgj772h6t23m2.public.blob.vercel-storage.com/static/sim.png" alt="Sim Studio Logo" width="500"/>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meant to be public, just incl our assets and generic images/videos

</p>

<p align="center">
Expand All @@ -15,7 +15,7 @@
</p>

<p align="center">
<img src="apps/sim/public/static/demo.gif" alt="Sim Studio Demo" width="800"/>
<img src="https://nwkhgj772h6t23m2.public.blob.vercel-storage.com/static/demo.gif" alt="Sim Studio Demo" width="800"/>
</p>

## Getting Started
Expand Down
24 changes: 17 additions & 7 deletions apps/docs/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ export function cn(...inputs: ClassValue[]) {
}

/**
* Get the full URL for a video asset stored in Vercel Blob
* Get the full URL for an asset stored in Vercel Blob or local fallback
* - If CDN is configured (NEXT_PUBLIC_BLOB_BASE_URL), uses CDN URL
* - Otherwise falls back to local static assets served from root path
*/
export function getVideoUrl(filename: string) {
const baseUrl = process.env.NEXT_PUBLIC_BLOB_BASE_URL
if (!baseUrl) {
console.warn('NEXT_PUBLIC_BLOB_BASE_URL not configured, falling back to local path')
return `/${filename}`
export function getAssetUrl(filename: string) {
const cdnBaseUrl = process.env.NEXT_PUBLIC_BLOB_BASE_URL
if (cdnBaseUrl) {
return `${cdnBaseUrl}/${filename}`
}
return `${baseUrl}/${filename}`
return `/${filename}`
}

/**
* Get the full URL for a video asset stored in Vercel Blob or local fallback
* - If CDN is configured (NEXT_PUBLIC_BLOB_BASE_URL), uses CDN URL
* - Otherwise falls back to local static assets served from root path
*/
export function getVideoUrl(filename: string) {
return getAssetUrl(filename)
}
Binary file removed apps/docs/public/api-deployment.mp4
Binary file not shown.
Binary file removed apps/docs/public/api-redeployment.mp4
Binary file not shown.
Binary file removed apps/docs/public/chat-input.mp4
Binary file not shown.
Binary file removed apps/docs/public/configure-schedule.mp4
Binary file not shown.
Binary file removed apps/docs/public/connections-resp-format.mp4
Binary file not shown.
Binary file removed apps/docs/public/connections.mp4
Binary file not shown.
Binary file removed apps/docs/public/granular-tool-control.mp4
Binary file not shown.
Binary file removed apps/docs/public/input-format.mp4
Binary file not shown.
Binary file removed apps/docs/public/models.mp4
Binary file not shown.
Binary file removed apps/docs/public/router-model-dropdown.mp4
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed apps/docs/public/tool-reordering.mp4
Binary file not shown.
Binary file removed apps/docs/public/tools.mp4
Binary file not shown.
Binary file removed apps/docs/public/variables-dropdown.mp4
Binary file not shown.
Binary file removed apps/docs/public/variables.mp4
Binary file not shown.
Binary file removed apps/docs/public/webhooks.mp4
Binary file not shown.
15 changes: 8 additions & 7 deletions apps/sim/app/(landing)/components/sections/blogs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { motion } from 'framer-motion'
import { getAssetUrl } from '@/lib/utils'
import { BlogCard } from '@/app/(landing)/components/blog-card'

function Blogs() {
Expand Down Expand Up @@ -50,7 +51,7 @@ function Blogs() {
date={new Date('25 April 2025')}
author='Emir Ayaz'
authorRole='Designer'
avatar='/static/sim.png'
avatar={getAssetUrl('static/sim.png')}
type='Agents'
readTime='6'
/>
Expand All @@ -61,7 +62,7 @@ function Blogs() {
date={new Date('25 April 2025')}
author='Emir Ayaz'
authorRole='Designer'
avatar='/static/sim.png'
avatar={getAssetUrl('static/sim.png')}
type='Agents'
readTime='6'
/>
Expand All @@ -80,18 +81,18 @@ function Blogs() {
date={new Date('25 April 2025')}
author='Emir Ayaz'
authorRole='Designer'
avatar='/static/sim.png'
avatar={getAssetUrl('static/sim.png')}
type='Agents'
readTime='6'
image='/static/hero.png'
image={getAssetUrl('static/hero.png')}
/>
<BlogCard
href='/blog/test'
title='How to Build an Agent in 5 Steps with SimStudio.ai'
description="Learn how to create a fully functional AI agent using SimStudio.ai's unified API and workflows."
author='Emir Ayaz'
authorRole='Designer'
avatar='/static/sim.png'
avatar={getAssetUrl('static/sim.png')}
type='Agents'
readTime='6'
/>
Expand All @@ -110,7 +111,7 @@ function Blogs() {
date={new Date('25 April 2025')}
author='Emir Ayaz'
authorRole='Designer'
avatar='/static/sim.png'
avatar={getAssetUrl('static/sim.png')}
type='Agents'
readTime='6'
/>
Expand All @@ -121,7 +122,7 @@ function Blogs() {
date={new Date('25 April 2025')}
author='Emir Ayaz'
authorRole='Designer'
avatar='/static/sim.png'
avatar={getAssetUrl('static/sim.png')}
type='Functions'
readTime='6'
/>
Expand Down
19 changes: 10 additions & 9 deletions apps/sim/app/(landing)/components/sections/testimonials.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { motion } from 'framer-motion'
import { getAssetUrl } from '@/lib/utils'
import useIsMobile from '@/app/(landing)/components/hooks/use-is-mobile'
import { Marquee } from '@/app/(landing)/components/magicui/marquee'

Expand All @@ -10,63 +11,63 @@ const X_TESTIMONIALS = [
username: '@GithubProjects',
viewCount: '90.4k',
tweetUrl: 'https://x.com/GithubProjects/status/1906383555707490499',
profileImage: '/twitter/github-projects.jpg',
profileImage: getAssetUrl('twitter/github-projects.jpg'),
},
{
text: 'A very good looking agent workflow builder 🔥 and open source!',
username: '@xyflowdev',
viewCount: '3,246',
tweetUrl: 'https://x.com/xyflowdev/status/1909501499719438670',
profileImage: '/twitter/xyflow.jpg',
profileImage: getAssetUrl('twitter/xyflow.jpg'),
},
{
text: "🚨 BREAKING: This startup just dropped the fastest way to build AI agents.\n\nThis Figma-like canvas to build agents will blow your mind.\n\nHere's why this is the best tool for building AI agents:",
username: '@hasantoxr',
viewCount: '515k',
tweetUrl: 'https://x.com/hasantoxr/status/1912909502036525271',
profileImage: '/twitter/hasan.jpg',
profileImage: getAssetUrl('twitter/hasan.jpg'),
},
{
text: 'omfggggg this is the zapier of agent building\n\ni always believed that building agents and using ai should not be limited to technical people. i think this solves just that\n\nthe fact that this is also open source makes me so optimistic about the future of building with ai :)))\n\ncongrats @karabegemir & @typingwala !!!',
username: '@nizzyabi',
viewCount: '6,269',
tweetUrl: 'https://x.com/nizzyabi/status/1907864421227180368',
profileImage: '/twitter/nizzy.jpg',
profileImage: getAssetUrl('twitter/nizzy.jpg'),
},
{
text: "One of the best products I've seen in the space, and the hustle and grind I've seen from @karabegemir and @typingwala is insane. Sim Studio is positioned to build something game-changing, and there's no better team for the job.\n\nCongrats on the launch 🚀 🎊 great things ahead!",
username: '@firestorm776',
viewCount: '956',
tweetUrl: 'https://x.com/firestorm776/status/1907896097735061598',
profileImage: '/twitter/samarth.jpg',
profileImage: getAssetUrl('twitter/samarth.jpg'),
},
{
text: 'lfgg got access to @simstudioai via @zerodotemail 😎',
username: '@nizzyabi',
viewCount: '1,585',
tweetUrl: 'https://x.com/nizzyabi/status/1910482357821595944',
profileImage: '/twitter/nizzy.jpg',
profileImage: getAssetUrl('twitter/nizzy.jpg'),
},
{
text: 'Feels like we\'re finally getting a "Photoshop moment" for AI devs—visual, intuitive, and fast enough to keep up with ideas mid-flow.',
username: '@syamrajk',
viewCount: '2,643',
tweetUrl: 'https://x.com/syamrajk/status/1912911980110946491',
profileImage: '/twitter/syamrajk.jpg',
profileImage: getAssetUrl('twitter/syamrajk.jpg'),
},
{
text: "🚨 BREAKING: This startup just dropped the fastest way to build AI agents.\n\nThis Figma-like canvas to build agents will blow your mind.\n\nHere's why this is the best tool for building AI agents:",
username: '@lazukars',
viewCount: '47.4k',
tweetUrl: 'https://x.com/lazukars/status/1913136390503600575',
profileImage: '/twitter/lazukars.png',
profileImage: getAssetUrl('twitter/lazukars.png'),
},
{
text: 'The use cases are endless. Great work @simstudioai',
username: '@daniel_zkim',
viewCount: '103',
tweetUrl: 'https://x.com/daniel_zkim/status/1907891273664782708',
profileImage: '/twitter/daniel.jpg',
profileImage: getAssetUrl('twitter/daniel.jpg'),
},
]

Expand Down
7 changes: 4 additions & 3 deletions apps/sim/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Metadata, Viewport } from 'next'
import { PublicEnvScript } from 'next-runtime-env'
import { env, isTruthy } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getAssetUrl } from '@/lib/utils'
import { TelemetryConsentDialog } from '@/app/telemetry-consent-dialog'
import '@/app/globals.css'

Expand Down Expand Up @@ -118,7 +119,7 @@ export const metadata: Metadata = {
siteName: 'Sim Studio',
images: [
{
url: 'https://simstudio.ai/social/facebook.png',
url: getAssetUrl('social/facebook.png'),
width: 1200,
height: 630,
alt: 'Sim Studio',
Expand All @@ -130,7 +131,7 @@ export const metadata: Metadata = {
title: 'Sim Studio',
description:
'Build and deploy AI agents using our Figma-like canvas. Build, write evals, and deploy AI agent workflows that automate workflows and streamline your business processes.',
images: ['https://simstudio.ai/social/twitter.png'],
images: [getAssetUrl('social/twitter.png')],
creator: '@simstudioai',
site: '@simstudioai',
},
Expand Down Expand Up @@ -231,7 +232,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<meta name='twitter:domain' content='simstudio.ai' />

{/* Additional image sources */}
<link rel='image_src' href='https://simstudio.ai/social/facebook.png' />
<link rel='image_src' href={getAssetUrl('social/facebook.png')} />

<PublicEnvScript />
</head>
Expand Down
7 changes: 4 additions & 3 deletions apps/sim/components/emails/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Container, Img, Link, Section, Text } from '@react-email/components'
import { env } from '@/lib/env'
import { getAssetUrl } from '@/lib/utils'

interface UnsubscribeOptions {
unsubscribeToken?: string
Expand All @@ -25,13 +26,13 @@ export const EmailFooter = ({
<tr>
<td align='center' style={{ padding: '0 8px' }}>
<Link href='https://x.com/simstudioai' rel='noopener noreferrer'>
<Img src={`${baseUrl}/static/x-icon.png`} width='24' height='24' alt='X' />
<Img src={getAssetUrl('static/x-icon.png')} width='24' height='24' alt='X' />
</Link>
</td>
<td align='center' style={{ padding: '0 8px' }}>
<Link href='https://discord.gg/Hr4UWYEcTT' rel='noopener noreferrer'>
<Img
src={`${baseUrl}/static/discord-icon.png`}
src={getAssetUrl('static/discord-icon.png')}
width='24'
height='24'
alt='Discord'
Expand All @@ -41,7 +42,7 @@ export const EmailFooter = ({
<td align='center' style={{ padding: '0 8px' }}>
<Link href='https://github.com/simstudioai/sim' rel='noopener noreferrer'>
<Img
src={`${baseUrl}/static/github-icon.png`}
src={getAssetUrl('static/github-icon.png')}
width='24'
height='24'
alt='GitHub'
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/components/emails/invitation-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '@react-email/components'
import { format } from 'date-fns'
import { env } from '@/lib/env'
import { getAssetUrl } from '@/lib/utils'
import { baseStyles } from './base-styles'
import EmailFooter from './footer'

Expand Down Expand Up @@ -59,7 +60,7 @@ export const InvitationEmail = ({
<Row>
<Column style={{ textAlign: 'center' }}>
<Img
src={`${baseUrl}/static/sim.png`}
src={getAssetUrl('static/sim.png')}
width='114'
alt='Sim Studio'
style={{
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/components/emails/otp-verification-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Text,
} from '@react-email/components'
import { env } from '@/lib/env'
import { getAssetUrl } from '@/lib/utils'
import { baseStyles } from './base-styles'
import EmailFooter from './footer'

Expand Down Expand Up @@ -68,7 +69,7 @@ export const OTPVerificationEmail = ({
<Row>
<Column style={{ textAlign: 'center' }}>
<Img
src={`${baseUrl}/static/sim.png`}
src={getAssetUrl('static/sim.png')}
width='114'
alt='Sim Studio'
style={{
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/components/emails/reset-password-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '@react-email/components'
import { format } from 'date-fns'
import { env } from '@/lib/env'
import { getAssetUrl } from '@/lib/utils'
import { baseStyles } from './base-styles'
import EmailFooter from './footer'

Expand All @@ -39,7 +40,7 @@ export const ResetPasswordEmail = ({
<Row>
<Column style={{ textAlign: 'center' }}>
<Img
src={`${baseUrl}/static/sim.png`}
src={getAssetUrl('static/sim.png')}
width='114'
alt='Sim Studio'
style={{
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/components/emails/workspace-invitation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Text,
} from '@react-email/components'
import { env } from '@/lib/env'
import { getAssetUrl } from '@/lib/utils'
import { baseStyles } from './base-styles'
import EmailFooter from './footer'

Expand Down Expand Up @@ -56,7 +57,7 @@ export const WorkspaceInvitationEmail = ({
<Row>
<Column style={{ textAlign: 'center' }}>
<Img
src={`${baseUrl}/static/sim.png`}
src={getAssetUrl('static/sim.png')}
width='114'
alt='Sim Studio'
style={{
Expand Down
4 changes: 4 additions & 0 deletions apps/sim/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ export const env = createEnv({
NEXT_PUBLIC_SENTRY_DSN: z.string().url().optional(), // Sentry DSN for client-side error tracking
NEXT_PUBLIC_SOCKET_URL: z.string().url().optional(), // WebSocket server URL for real-time features

// Asset Storage
NEXT_PUBLIC_BLOB_BASE_URL: z.string().url().optional(), // Base URL for Vercel Blob storage (CDN assets)

// Google Services - For client-side Google integrations
NEXT_PUBLIC_GOOGLE_CLIENT_ID: z.string().optional(), // Google OAuth client ID for browser auth
NEXT_PUBLIC_GOOGLE_API_KEY: z.string().optional(), // Google API key for client-side API calls
Expand All @@ -168,6 +171,7 @@ export const env = createEnv({
NEXT_PUBLIC_APP_URL: process.env.NEXT_PUBLIC_APP_URL,
NEXT_PUBLIC_VERCEL_URL: process.env.NEXT_PUBLIC_VERCEL_URL,
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_BLOB_BASE_URL: process.env.NEXT_PUBLIC_BLOB_BASE_URL,
NEXT_PUBLIC_GOOGLE_CLIENT_ID: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
NEXT_PUBLIC_GOOGLE_API_KEY: process.env.NEXT_PUBLIC_GOOGLE_API_KEY,
NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER: process.env.NEXT_PUBLIC_GOOGLE_PROJECT_NUMBER,
Expand Down
1 change: 1 addition & 0 deletions apps/sim/lib/security/csp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const cspDirectives: CSPDirectives = {
'https://*.atlassian.com',
'https://cdn.discordapp.com',
'https://*.githubusercontent.com',
'https://*.public.blob.vercel-storage.com',
],

'media-src': ["'self'", 'blob:'],
Expand Down
13 changes: 13 additions & 0 deletions apps/sim/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,19 @@ export function getInvalidCharacters(name: string): string[] {
return invalidChars ? [...new Set(invalidChars)] : []
}

/**
* Get the full URL for an asset stored in Vercel Blob or local fallback
* - If CDN is configured (NEXT_PUBLIC_BLOB_BASE_URL), uses CDN URL
* - Otherwise falls back to local static assets served from root path
*/
export function getAssetUrl(filename: string) {
const cdnBaseUrl = env.NEXT_PUBLIC_BLOB_BASE_URL
if (cdnBaseUrl) {
return `${cdnBaseUrl}/${filename}`
}
return `/${filename}`
}

/**
* No-operation function for use as default callback
*/
Expand Down
Binary file removed apps/sim/public/static/demo.gif
Binary file not shown.