Compare commits

...

2 Commits

Author SHA1 Message Date
Cursor Agent
9f56602c98 fix: revert hero CTA href from /enterprise to /login
The /enterprise route does not exist - the Enterprise component is a stub
returning null and there is no enterprise/page.tsx. Reverting to the
original working /login route to fix the broken CTA.
2026-03-14 22:10:30 +00:00
Emir Karabeg
dc98812bd7 improvement: landing, sidebar, globals, buttons 2026-03-14 15:03:53 -07:00
76 changed files with 479 additions and 270 deletions

View File

@@ -274,7 +274,7 @@ export default function Collaboration() {
<Link
href='/signup'
className='group/cta mt-[12px] inline-flex h-[32px] cursor-none items-center gap-[6px] rounded-[5px] border border-[#33C482] bg-[#33C482] px-[10px] font-[430] font-season text-[14px] text-black transition-[filter] hover:brightness-110'
className='group/cta mt-[12px] inline-flex h-[32px] cursor-none items-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
Build together
<span className='relative h-[10px] w-[10px] shrink-0'>

View File

@@ -79,7 +79,7 @@ export default function Hero() {
</Link>
<Link
href='/signup'
className={`${CTA_BASE} gap-[8px] border-[#33C482] bg-[#33C482] text-black transition-[filter] hover:brightness-110`}
className={`${CTA_BASE} gap-[8px] border-[#FFFFFF] bg-[#FFFFFF] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]`}
aria-label='Get started with Sim'
>
Get started

View File

@@ -0,0 +1,98 @@
'use client'
import { memo, useCallback, useRef, useState } from 'react'
import { ArrowUp } from 'lucide-react'
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 C = {
SURFACE: '#292929',
BORDER: '#3d3d3d',
TEXT_PRIMARY: '#e6e6e6',
} as const
/**
* Landing preview replica of the workspace Home initial view.
* Shows a greeting heading and a minimal chat input (no + or mic).
* On submit, stores the prompt and redirects to /signup.
*/
export const LandingPreviewHome = memo(function LandingPreviewHome() {
const landingSubmit = useLandingSubmit()
const [inputValue, setInputValue] = useState('')
const textareaRef = useRef<HTMLTextAreaElement>(null)
const animatedPlaceholder = useAnimatedPlaceholder()
const isEmpty = inputValue.trim().length === 0
const handleSubmit = useCallback(() => {
if (isEmpty) return
landingSubmit(inputValue)
}, [isEmpty, inputValue, landingSubmit])
const MAX_HEIGHT = 200
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSubmit()
}
},
[handleSubmit]
)
const handleInput = useCallback((e: React.FormEvent<HTMLTextAreaElement>) => {
const target = e.target as HTMLTextAreaElement
target.style.height = 'auto'
target.style.height = `${Math.min(target.scrollHeight, MAX_HEIGHT)}px`
}, [])
return (
<div className='flex min-w-0 flex-1 flex-col items-center justify-center px-[24px] pb-[2vh]'>
<h1
className='mb-[24px] max-w-[42rem] font-[430] font-season text-[32px] tracking-[-0.02em]'
style={{ color: C.TEXT_PRIMARY }}
>
What should we get done?
</h1>
<div className='w-full max-w-[32rem]'>
<div
className='cursor-text rounded-[20px] border px-[10px] py-[8px]'
style={{ borderColor: C.BORDER, backgroundColor: C.SURFACE }}
onClick={() => textareaRef.current?.focus()}
>
<textarea
ref={textareaRef}
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
onInput={handleInput}
placeholder={animatedPlaceholder}
rows={1}
className='m-0 box-border min-h-[24px] w-full resize-none overflow-y-auto border-0 bg-transparent px-[4px] py-[4px] font-body text-[15px] leading-[24px] tracking-[-0.015em] outline-none placeholder:font-[380] placeholder:text-[#787878] focus-visible:ring-0'
style={{
color: C.TEXT_PRIMARY,
caretColor: C.TEXT_PRIMARY,
maxHeight: `${MAX_HEIGHT}px`,
}}
/>
<div className='flex items-center justify-end'>
<button
type='button'
onClick={handleSubmit}
disabled={isEmpty}
className='flex h-[28px] w-[28px] items-center justify-center rounded-full border-0 p-0 transition-colors'
style={{
background: isEmpty ? '#808080' : '#e0e0e0',
cursor: isEmpty ? 'not-allowed' : 'pointer',
}}
>
<ArrowUp size={16} strokeWidth={2.25} color='#1b1b1b' />
</button>
</div>
</div>
</div>
</div>
)
})

View File

@@ -8,6 +8,23 @@ import { createPortal } from 'react-dom'
import { BubbleChatPreview, ChevronDown, MoreHorizontal, Play } from '@/components/emcn'
import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
/**
* Stores the prompt in browser storage and redirects to /signup.
* Shared by both the copilot panel and the landing home view.
*/
export function useLandingSubmit() {
const router = useRouter()
return useCallback(
(text: string) => {
const trimmed = text.trim()
if (!trimmed) return
LandingPromptStorage.store(trimmed)
router.push('/signup')
},
[router]
)
}
/**
* Lightweight static panel replicating the real workspace panel styling.
* The copilot tab is active with a functional user input.
@@ -18,7 +35,7 @@ import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
* inside Content > Copilot > header-bar(mx-[-1px]) > UserInput(p-8)
*/
export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
const router = useRouter()
const landingSubmit = useLandingSubmit()
const [inputValue, setInputValue] = useState('')
const textareaRef = useRef<HTMLTextAreaElement>(null)
const [cursorPos, setCursorPos] = useState<{ x: number; y: number } | null>(null)
@@ -27,9 +44,8 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
const handleSubmit = useCallback(() => {
if (isEmpty) return
LandingPromptStorage.store(inputValue)
router.push('/signup')
}, [isEmpty, inputValue, router])
landingSubmit(inputValue)
}, [isEmpty, inputValue, landingSubmit])
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
@@ -60,10 +76,10 @@ export const LandingPreviewPanel = memo(function LandingPreviewPanel() {
onMouseMove={(e) => setCursorPos({ x: e.clientX, y: e.clientY })}
onMouseLeave={() => setCursorPos(null)}
>
<div className='flex h-[30px] items-center rounded-[5px] bg-[#32bd7e] px-[10px] transition-[filter] hover:brightness-110'>
<div className='flex h-[30px] items-center rounded-[5px] bg-[#33C482] px-[10px] transition-colors hover:bg-[#2DAC72]'>
<span className='font-medium text-[#1b1b1b] text-[12px]'>Deploy</span>
</div>
<div className='flex h-[30px] items-center gap-[8px] rounded-[5px] bg-[#32bd7e] px-[10px] transition-[filter] hover:brightness-110'>
<div className='flex h-[30px] items-center gap-[8px] rounded-[5px] bg-[#33C482] px-[10px] transition-colors hover:bg-[#2DAC72]'>
<Play className='h-[11.5px] w-[11.5px] text-[#1b1b1b]' />
<span className='font-medium text-[#1b1b1b] text-[12px]'>Run</span>
</div>

View File

@@ -1,141 +1,204 @@
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import { Database, Layout, Search, Settings } from 'lucide-react'
import { ChevronDown, Library } from '@/components/emcn'
import { ChevronDown, Home, Library } from '@/components/emcn'
import {
Calendar,
Database,
File,
HelpCircle,
Search,
Settings,
Table,
} from '@/components/emcn/icons'
import type { PreviewWorkflow } from '@/app/(home)/components/landing-preview/components/landing-preview-workflow/workflow-data'
/**
* Props for the LandingPreviewSidebar component
*/
interface LandingPreviewSidebarProps {
workflows: PreviewWorkflow[]
activeWorkflowId: string
activeView: 'home' | 'workflow'
onSelectWorkflow: (id: string) => void
onSelectHome: () => void
}
/**
* Static footer navigation items matching the real sidebar
* Hardcoded dark-theme equivalents of the real sidebar CSS variables.
* The preview lives inside a `dark` wrapper but CSS variable cascade
* isn't guaranteed, so we pin the hex values directly.
*/
const FOOTER_NAV_ITEMS = [
{ id: 'logs', label: 'Logs', icon: Library },
{ id: 'templates', label: 'Templates', icon: Layout },
const C = {
SURFACE_1: '#1e1e1e',
SURFACE_2: '#252525',
SURFACE_ACTIVE: '#363636',
BORDER: '#2c2c2c',
TEXT_PRIMARY: '#e6e6e6',
TEXT_BODY: '#cdcdcd',
TEXT_ICON: '#939393',
BRAND: '#33C482',
} as const
const WORKSPACE_NAV = [
{ id: 'tables', label: 'Tables', icon: Table },
{ id: 'files', label: 'Files', icon: File },
{ id: 'knowledge-base', label: 'Knowledge Base', icon: Database },
{ id: 'scheduled-tasks', label: 'Scheduled Tasks', icon: Calendar },
{ id: 'logs', label: 'Logs', icon: Library },
] as const
const FOOTER_NAV = [
{ id: 'help', label: 'Help', icon: HelpCircle },
{ id: 'settings', label: 'Settings', icon: Settings },
] as const
function StaticNavItem({
icon: Icon,
label,
}: {
icon: React.ComponentType<{ className?: string; style?: React.CSSProperties }>
label: string
}) {
return (
<div className='pointer-events-none mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px]'>
<Icon className='h-[14px] w-[14px] flex-shrink-0' style={{ color: C.TEXT_ICON }} />
<span className='truncate text-[13px]' style={{ color: C.TEXT_BODY, fontWeight: 450 }}>
{label}
</span>
</div>
)
}
/**
* Lightweight static sidebar replicating the real workspace sidebar styling.
* Lightweight sidebar replicating the real workspace sidebar layout and sizing.
* Starts from the workspace header (no logo/collapse row).
* Only workflow items are interactive — everything else is pointer-events-none.
*
* Colors sourced from the dark theme CSS variables:
* --surface-1: #1e1e1e, --surface-5: #363636, --border: #2c2c2c, --border-1: #3d3d3d
* --text-primary: #e6e6e6, --text-tertiary: #b3b3b3, --text-muted: #787878
*/
export function LandingPreviewSidebar({
workflows,
activeWorkflowId,
activeView,
onSelectWorkflow,
onSelectHome,
}: LandingPreviewSidebarProps) {
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const handleToggle = useCallback(() => {
setIsDropdownOpen((prev) => !prev)
}, [])
useEffect(() => {
if (!isDropdownOpen) return
const handleClickOutside = (e: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setIsDropdownOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside)
}, [isDropdownOpen])
const isHomeActive = activeView === 'home'
return (
<div className='flex h-full w-[220px] flex-shrink-0 flex-col border-[#2c2c2c] border-r bg-[#1e1e1e]'>
{/* Header */}
<div className='relative flex-shrink-0 px-[14px] pt-[12px]' ref={dropdownRef}>
<div className='flex items-center justify-between'>
<button
type='button'
onClick={handleToggle}
className='group -mx-[6px] flex cursor-pointer items-center gap-[8px] rounded-[6px] bg-transparent px-[6px] py-[4px] transition-colors hover:bg-[#363636]'
<div
className='flex h-full w-[248px] flex-shrink-0 flex-col pt-[12px] tracking-[0.02em]'
style={{ backgroundColor: C.SURFACE_1 }}
>
{/* Workspace Header */}
<div className='flex-shrink-0 px-[10px]'>
<div
className='pointer-events-none flex h-[32px] w-full items-center gap-[8px] rounded-[8px] border pr-[8px] pl-[5px]'
style={{ borderColor: C.BORDER, backgroundColor: C.SURFACE_2 }}
>
<div className='flex h-[20px] w-[20px] flex-shrink-0 items-center justify-center rounded-[4px] bg-white'>
<svg width='10' height='10' viewBox='0 0 10 10' fill='none'>
<path
d='M1 9C1 4.58 4.58 1 9 1'
stroke='#1e1e1e'
strokeWidth='1.8'
strokeLinecap='round'
/>
</svg>
</div>
<span
className='min-w-0 flex-1 truncate text-left font-medium text-[13px]'
style={{ color: C.TEXT_PRIMARY }}
>
<span className='truncate font-base text-[#e6e6e6] text-[14px]'>My Workspace</span>
<ChevronDown
className={`h-[8px] w-[10px] flex-shrink-0 text-[#787878] transition-all duration-100 group-hover:text-[#cccccc] ${isDropdownOpen ? 'rotate-180' : ''}`}
/>
</button>
<div className='pointer-events-none flex flex-shrink-0 items-center'>
<Search className='h-[14px] w-[14px] text-[#787878]' />
Superark
</span>
<ChevronDown className='h-[8px] w-[10px] flex-shrink-0' style={{ color: C.TEXT_ICON }} />
</div>
</div>
{/* Top Navigation: Home (interactive), Search (static) */}
<div className='mt-[10px] flex flex-shrink-0 flex-col gap-[2px] px-[8px]'>
<button
type='button'
onClick={onSelectHome}
className='mx-[2px] flex h-[28px] items-center gap-[8px] rounded-[8px] px-[8px] transition-colors'
style={{ backgroundColor: isHomeActive ? C.SURFACE_ACTIVE : 'transparent' }}
onMouseEnter={(e) => {
if (!isHomeActive) e.currentTarget.style.backgroundColor = C.SURFACE_ACTIVE
}}
onMouseLeave={(e) => {
if (!isHomeActive) e.currentTarget.style.backgroundColor = 'transparent'
}}
>
<Home className='h-[14px] w-[14px] flex-shrink-0' style={{ color: C.TEXT_ICON }} />
<span className='truncate text-[13px]' style={{ color: C.TEXT_BODY, fontWeight: 450 }}>
Home
</span>
</button>
<StaticNavItem icon={Search} label='Search' />
</div>
{/* Workspace */}
<div className='mt-[14px] flex flex-shrink-0 flex-col'>
<div className='px-[16px] pb-[6px]'>
<div className='font-base text-[12px]' style={{ color: C.TEXT_ICON }}>
Workspace
</div>
</div>
<div className='flex flex-col gap-[2px] px-[8px]'>
{WORKSPACE_NAV.map((item) => (
<StaticNavItem key={item.id} icon={item.icon} label={item.label} />
))}
</div>
</div>
{/* Workspace switcher dropdown */}
{isDropdownOpen && (
<div className='absolute top-[42px] left-[8px] z-50 min-w-[160px] max-w-[160px] rounded-[6px] bg-[#242424] px-[6px] py-[6px] shadow-lg'>
<div
className='flex h-[26px] cursor-pointer items-center gap-[8px] rounded-[6px] bg-[#3d3d3d] px-[6px] font-base text-[#e6e6e6] text-[13px]'
role='menuitem'
onClick={() => setIsDropdownOpen(false)}
>
<span className='min-w-0 flex-1 truncate'>My Workspace</span>
{/* Scrollable Tasks + Workflows */}
<div className='flex flex-1 flex-col overflow-y-auto overflow-x-hidden pt-[14px]'>
{/* Workflows */}
<div className='flex flex-col'>
<div className='px-[16px]'>
<div className='font-base text-[12px]' style={{ color: C.TEXT_ICON }}>
Workflows
</div>
</div>
)}
</div>
{/* Workflow items */}
<div className='mt-[8px] space-y-[2px] overflow-x-hidden px-[8px]'>
{workflows.map((workflow) => {
const isActive = workflow.id === activeWorkflowId
return (
<button
key={workflow.id}
type='button'
onClick={() => onSelectWorkflow(workflow.id)}
className={`group flex h-[26px] w-full items-center gap-[8px] rounded-[8px] px-[6px] text-[14px] transition-colors ${
isActive ? 'bg-[#363636]' : 'bg-transparent hover:bg-[#363636]'
}`}
>
<div
className='h-[14px] w-[14px] flex-shrink-0 rounded-[4px]'
style={{ backgroundColor: workflow.color }}
/>
<div className='min-w-0 flex-1'>
<div
className={`min-w-0 truncate text-left font-medium ${
isActive ? 'text-[#e6e6e6]' : 'text-[#b3b3b3] group-hover:text-[#e6e6e6]'
}`}
<div className='mt-[6px] flex flex-col gap-[2px] px-[8px]'>
{workflows.map((workflow) => {
const isActive = activeView === 'workflow' && workflow.id === activeWorkflowId
return (
<button
key={workflow.id}
type='button'
onClick={() => onSelectWorkflow(workflow.id)}
className='group mx-[2px] flex h-[28px] w-full items-center gap-[8px] rounded-[8px] px-[8px] transition-colors'
style={{ backgroundColor: isActive ? C.SURFACE_ACTIVE : 'transparent' }}
onMouseEnter={(e) => {
if (!isActive) e.currentTarget.style.backgroundColor = C.SURFACE_ACTIVE
}}
onMouseLeave={(e) => {
if (!isActive) e.currentTarget.style.backgroundColor = 'transparent'
}}
>
{workflow.name}
</div>
</div>
</button>
)
})}
<div
className='h-[14px] w-[14px] flex-shrink-0 rounded-[4px] border-[2.5px]'
style={{
backgroundColor: workflow.color,
borderColor: `${workflow.color}60`,
backgroundClip: 'padding-box',
}}
/>
<div
className='min-w-0 flex-1 truncate text-left text-[13px]'
style={{ color: C.TEXT_BODY, fontWeight: 450 }}
>
{workflow.name}
</div>
</button>
)
})}
</div>
</div>
</div>
{/* Footer navigation — static */}
<div className='pointer-events-none mt-auto flex flex-shrink-0 flex-col gap-[2px] border-[#2c2c2c] border-t px-[7.75px] pt-[8px] pb-[8px]'>
{FOOTER_NAV_ITEMS.map((item) => {
const Icon = item.icon
return (
<div
key={item.id}
className='flex h-[26px] items-center gap-[8px] rounded-[8px] px-[6px] text-[14px]'
>
<Icon className='h-[14px] w-[14px] flex-shrink-0 text-[#b3b3b3]' />
<span className='truncate font-medium text-[#b3b3b3] text-[13px]'>{item.label}</span>
</div>
)
})}
{/* Footer */}
<div className='flex flex-shrink-0 flex-col gap-[2px] px-[8px] pt-[9px] pb-[8px]'>
{FOOTER_NAV.map((item) => (
<StaticNavItem key={item.id} icon={item.icon} label={item.label} />
))}
</div>
</div>
)

View File

@@ -4,6 +4,7 @@ import { memo } from 'react'
import { motion } from 'framer-motion'
import { Database } from 'lucide-react'
import { Handle, type NodeProps, Position } from 'reactflow'
import { Blimp } from '@/components/emcn'
import {
AgentIcon,
AnthropicIcon,
@@ -63,6 +64,7 @@ const BLOCK_ICONS: Record<string, React.ComponentType<{ className?: string }>> =
reducto: ReductoIcon,
textract: TextractIcon,
linkedin: LinkedInIcon,
mothership: Blimp,
}
/** Model prefix → provider icon for the "Model" row in agent blocks. */

View File

@@ -91,11 +91,11 @@ const IT_SERVICE_WORKFLOW: PreviewWorkflow = {
}
/**
* Content pipeline workflow — Schedule -> Agent (X + YouTube tools)
* Self-healing CRM workflow — Schedule -> Mothership
*/
const CONTENT_PIPELINE_WORKFLOW: PreviewWorkflow = {
id: 'wf-content-pipeline',
name: 'Content Pipeline',
const SELF_HEALING_CRM_WORKFLOW: PreviewWorkflow = {
id: 'wf-self-healing-crm',
name: 'Self-healing CRM',
color: '#33C482',
blocks: [
{
@@ -111,23 +111,16 @@ const CONTENT_PIPELINE_WORKFLOW: PreviewWorkflow = {
hideTargetHandle: true,
},
{
id: 'agent-2',
name: 'Agent',
type: 'agent',
bgColor: '#701ffc',
rows: [
{ title: 'Model', value: 'grok-4' },
{ title: 'System Prompt', value: 'Repurpose trending...' },
],
tools: [
{ name: 'X', type: 'x', bgColor: '#000000' },
{ name: 'YouTube', type: 'youtube', bgColor: '#FF0000' },
],
id: 'mothership-1',
name: 'Update Agent',
type: 'mothership',
bgColor: '#33C482',
rows: [{ title: 'Prompt', value: 'Audit CRM records, fix...' }],
position: { x: 420, y: 180 },
hideSourceHandle: true,
},
],
edges: [{ id: 'e-3', source: 'schedule-1', target: 'agent-2' }],
edges: [{ id: 'e-3', source: 'schedule-1', target: 'mothership-1' }],
}
/**
@@ -154,7 +147,7 @@ const NEW_AGENT_WORKFLOW: PreviewWorkflow = {
}
export const PREVIEW_WORKFLOWS: PreviewWorkflow[] = [
CONTENT_PIPELINE_WORKFLOW,
SELF_HEALING_CRM_WORKFLOW,
IT_SERVICE_WORKFLOW,
NEW_AGENT_WORKFLOW,
]

View File

@@ -1,7 +1,8 @@
'use client'
import { useEffect, useRef, useState } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { motion, type Variants } from 'framer-motion'
import { LandingPreviewHome } from '@/app/(home)/components/landing-preview/components/landing-preview-home/landing-preview-home'
import { LandingPreviewPanel } from '@/app/(home)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
import { LandingPreviewSidebar } from '@/app/(home)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar'
import { LandingPreviewWorkflow } from '@/app/(home)/components/landing-preview/components/landing-preview-workflow/landing-preview-workflow'
@@ -56,6 +57,7 @@ const panelVariants: Variants = {
* load — workflow switches render instantly.
*/
export function LandingPreview() {
const [activeView, setActiveView] = useState<'home' | 'workflow'>('workflow')
const [activeWorkflowId, setActiveWorkflowId] = useState(PREVIEW_WORKFLOWS[0].id)
const isInitialMount = useRef(true)
@@ -63,12 +65,23 @@ export function LandingPreview() {
isInitialMount.current = false
}, [])
const handleSelectWorkflow = useCallback((id: string) => {
setActiveWorkflowId(id)
setActiveView('workflow')
}, [])
const handleSelectHome = useCallback(() => {
setActiveView('home')
}, [])
const activeWorkflow =
PREVIEW_WORKFLOWS.find((w) => w.id === activeWorkflowId) ?? PREVIEW_WORKFLOWS[0]
const isWorkflowView = activeView === 'workflow'
return (
<motion.div
className='dark flex aspect-[1116/549] w-full overflow-hidden rounded bg-[#1b1b1b] antialiased'
className='dark flex aspect-[1116/549] w-full overflow-hidden rounded bg-[#1e1e1e] antialiased'
initial='hidden'
animate='visible'
variants={containerVariants}
@@ -77,15 +90,34 @@ export function LandingPreview() {
<LandingPreviewSidebar
workflows={PREVIEW_WORKFLOWS}
activeWorkflowId={activeWorkflowId}
onSelectWorkflow={setActiveWorkflowId}
activeView={activeView}
onSelectWorkflow={handleSelectWorkflow}
onSelectHome={handleSelectHome}
/>
</motion.div>
<div className='relative flex-1 overflow-hidden'>
<LandingPreviewWorkflow workflow={activeWorkflow} animate={isInitialMount.current} />
<div className='flex min-w-0 flex-1 flex-col p-[8px] pl-0'>
<div className='flex flex-1 overflow-hidden rounded-[8px] border border-[#2c2c2c] bg-[#1b1b1b]'>
<div
className={
isWorkflowView
? 'relative min-w-0 flex-1 overflow-hidden'
: 'relative flex min-w-0 flex-1 flex-col overflow-hidden'
}
>
{isWorkflowView ? (
<LandingPreviewWorkflow workflow={activeWorkflow} animate={isInitialMount.current} />
) : (
<LandingPreviewHome />
)}
</div>
<motion.div
className={isWorkflowView ? 'hidden lg:flex' : 'hidden'}
variants={panelVariants}
>
<LandingPreviewPanel />
</motion.div>
</div>
</div>
<motion.div className='hidden lg:flex' variants={panelVariants}>
<LandingPreviewPanel />
</motion.div>
</motion.div>
)
}

View File

@@ -86,7 +86,7 @@ export default function Navbar() {
</Link>
<Link
href='/signup'
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#33C482] bg-[#33C482] px-[9px] text-[13.5px] text-black transition-[filter] hover:brightness-110'
className='inline-flex h-[30px] items-center gap-[7px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[9px] text-[13.5px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
aria-label='Get started with Sim'
>
Get started

View File

@@ -123,7 +123,7 @@ function PricingCard({ tier }: PricingCardProps) {
) : isPro ? (
<Link
href={tier.cta.href}
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#33C482] bg-[#33C482] px-[10px] font-[430] font-season text-[14px] text-white transition-[filter] hover:brightness-110'
className='flex h-[32px] w-full items-center justify-center rounded-[5px] border border-[#1D1D1D] bg-[#1D1D1D] px-[10px] font-[430] font-season text-[14px] text-white transition-colors hover:border-[#2A2A2A] hover:bg-[#2A2A2A]'
>
{tier.cta.label}
</Link>

View File

@@ -557,7 +557,7 @@ export default function Templates() {
type='button'
onClick={handleUseTemplate}
disabled={isPreparingTemplate}
className='group/cta absolute top-[16px] right-[16px] z-10 inline-flex h-[32px] cursor-pointer items-center gap-[6px] rounded-[5px] border border-[#33C482] bg-[#33C482] px-[10px] font-[430] font-season text-[14px] text-black transition-[filter] hover:brightness-110'
className='group/cta absolute top-[16px] right-[16px] z-10 inline-flex h-[32px] cursor-pointer items-center gap-[6px] rounded-[5px] border border-[#FFFFFF] bg-[#FFFFFF] px-[10px] font-[430] font-season text-[14px] text-black transition-colors hover:border-[#E0E0E0] hover:bg-[#E0E0E0]'
>
{isPreparingTemplate ? 'Preparing...' : 'Use template'}
<span className='relative h-[10px] w-[10px] shrink-0'>

View File

@@ -41,6 +41,19 @@
opacity: 0;
}
@keyframes sidebar-collapse-guard {
from {
pointer-events: none;
}
to {
pointer-events: auto;
}
}
.sidebar-container[data-collapsed] {
animation: sidebar-collapse-guard 250ms step-end;
}
.sidebar-container.is-resizing {
transition: none;
}
@@ -145,7 +158,7 @@
--brand-400: #8e4cfb;
--brand-secondary: #33b4ff;
--brand-tertiary: #22c55e;
--brand-tertiary-2: #32bd7e;
--brand-tertiary-2: #33c482;
--selection: #1a5cf6;
--warning: #ea580c;
@@ -267,7 +280,7 @@
--brand-400: #8e4cfb;
--brand-secondary: #33b4ff;
--brand-tertiary: #22c55e;
--brand-tertiary-2: #32bd7e;
--brand-tertiary-2: #33c482;
--selection: #4b83f7;
--warning: #ff6600;

View File

@@ -171,7 +171,7 @@ export async function GET(request: NextRequest) {
([category, templates]) => `
<h2 style="margin-top: 24px; margin-bottom: 12px; font-size: 14px; color: #666; text-transform: uppercase; letter-spacing: 0.5px;">${category}</h2>
<ul style="list-style: none; padding: 0; margin: 0;">
${templates.map((t) => `<li style="margin: 8px 0;"><a href="?template=${t}" style="color: #32bd7e; text-decoration: none; font-size: 16px;">${t}</a></li>`).join('')}
${templates.map((t) => `<li style="margin: 8px 0;"><a href="?template=${t}" style="color: #33C482; text-decoration: none; font-size: 16px;">${t}</a></li>`).join('')}
</ul>
`
)

View File

@@ -11,7 +11,7 @@ interface ThankYouScreenProps {
}
/** Default green color matching --brand-tertiary-2 */
const DEFAULT_THANK_YOU_COLOR = '#32bd7e'
const DEFAULT_THANK_YOU_COLOR = '#33C482'
/** Legacy blue default that should be treated as "no custom color" */
const LEGACY_BLUE_DEFAULT = '#3972F6'

View File

@@ -1249,7 +1249,7 @@ export default function ResumeExecutionPage({
{message && <Badge variant='green'>{message}</Badge>}
{/* Action */}
<Button variant='tertiary' onClick={handleResume} disabled={resumeDisabled}>
<Button variant='primary' onClick={handleResume} disabled={resumeDisabled}>
{loadingAction ? 'Resuming...' : 'Resume Execution'}
</Button>
</>

View File

@@ -733,7 +733,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
<>
{!currentUserId ? (
<Button
variant='tertiary'
variant='primary'
onClick={() => {
const callbackUrl =
isWorkspaceContext && workspaceId
@@ -749,7 +749,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
</Button>
) : isWorkspaceContext ? (
<Button
variant='tertiary'
variant='primary'
onClick={handleUseTemplate}
disabled={isUsing}
className='!text-[#FFFFFF] h-[32px] rounded-[6px] px-[12px] text-[14px]'

View File

@@ -343,7 +343,7 @@ export function Home({ chatId }: HomeProps = {}) {
if (!hasMessages) {
return (
<div className='h-full overflow-y-auto bg-[var(--bg)]'>
<div className='h-full overflow-y-auto bg-[var(--bg)] [scrollbar-gutter:stable]'>
<div className='flex min-h-full flex-col items-center justify-center px-[24px] pb-[2vh]'>
<h1 className='mb-[24px] max-w-[42rem] font-[430] font-season text-[32px] text-[var(--text-primary)] tracking-[-0.02em]'>
What should we get done
@@ -372,7 +372,7 @@ export function Home({ chatId }: HomeProps = {}) {
<div className='flex h-full min-w-0 flex-1 flex-col'>
<div
ref={scrollContainerRef}
className='min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-6 pt-4 pb-8'
className='min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-6 pt-4 pb-8 [scrollbar-gutter:stable]'
>
<div className='mx-auto max-w-[42rem] space-y-6'>
{messages.map((msg, index) => {

View File

@@ -554,7 +554,7 @@ export function DocumentTagsModal({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={saveDocumentTag}
className='flex-1'
disabled={!canSaveTag}
@@ -718,7 +718,7 @@ export function DocumentTagsModal({
</Button>
)}
<Button
variant='tertiary'
variant='primary'
onClick={saveDocumentTag}
className='flex-1'
disabled={

View File

@@ -558,7 +558,7 @@ export function AddConnectorModal({ open, onOpenChange, knowledgeBaseId }: AddCo
<Button variant='default' onClick={() => onOpenChange(false)} disabled={isCreating}>
Cancel
</Button>
<Button variant='tertiary' onClick={handleSubmit} disabled={!canSubmit || isCreating}>
<Button variant='primary' onClick={handleSubmit} disabled={!canSubmit || isCreating}>
{isCreating ? (
<>
<Loader2 className='mr-1.5 h-3.5 w-3.5 animate-spin' />

View File

@@ -347,7 +347,7 @@ export function AddDocumentsModal({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
type='button'
onClick={handleUpload}
disabled={files.length === 0 || isUploading}

View File

@@ -387,7 +387,7 @@ export function BaseTagsModal({ open, onOpenChange, knowledgeBaseId }: BaseTagsM
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={saveTagDefinition}
className='flex-1'
disabled={

View File

@@ -161,7 +161,7 @@ export function EditConnectorModal({
<Button variant='default' onClick={() => onOpenChange(false)} disabled={isSaving}>
Cancel
</Button>
<Button variant='tertiary' onClick={handleSave} disabled={!hasChanges || isSaving}>
<Button variant='primary' onClick={handleSave} disabled={!hasChanges || isSaving}>
{isSaving ? (
<>
<Loader2 className='mr-1.5 h-3.5 w-3.5 animate-spin' />

View File

@@ -124,7 +124,7 @@ export function RenameDocumentModal({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
type='submit'
disabled={isSubmitting || !name?.trim() || name.trim() === initialName}
>

View File

@@ -522,7 +522,7 @@ export function CreateBaseModal({ open, onOpenChange }: CreateBaseModalProps) {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
type='submit'
disabled={isSubmitting || !nameValue?.trim()}
>

View File

@@ -159,7 +159,7 @@ export function EditKnowledgeBaseModal({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
type='submit'
disabled={isSubmitting || !nameValue?.trim() || !isDirty}
>

View File

@@ -580,7 +580,7 @@ export const NotificationSettings = memo(function NotificationSettings({
<div className='flex flex-shrink-0 items-center gap-[8px]'>
<Button
variant='tertiary'
variant='primary'
onClick={() => handleTest(subscription.id)}
disabled={testNotification.isPending && testStatus?.id !== subscription.id}
>
@@ -1235,7 +1235,7 @@ export const NotificationSettings = memo(function NotificationSettings({
</Button>
)}
<Button
variant='tertiary'
variant='primary'
onClick={handleSave}
disabled={createNotification.isPending || updateNotification.isPending}
>
@@ -1254,7 +1254,7 @@ export const NotificationSettings = memo(function NotificationSettings({
resetForm()
setShowForm(true)
}}
variant='tertiary'
variant='primary'
disabled={isLoading}
>
<Plus className='mr-[6px] h-[13px] w-[13px]' />

View File

@@ -541,7 +541,7 @@ export function ScheduleModal({ open, onOpenChange, workspaceId, schedule }: Sch
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleSubmit}
disabled={
!isFormValid || createScheduleMutation.isPending || updateScheduleMutation.isPending

View File

@@ -149,7 +149,7 @@ export function ApiKeys() {
e.currentTarget.blur()
setIsCreateDialogOpen(true)
}}
variant='tertiary'
variant='primary'
disabled={createButtonDisabled}
>
<Plus className='mr-[6px] h-[13px] w-[13px]' />

View File

@@ -190,7 +190,7 @@ export function CreateApiKeyModal({
</Button>
<Button
type='button'
variant='tertiary'
variant='primary'
onClick={handleCreateKey}
disabled={
!keyName.trim() ||

View File

@@ -277,11 +277,7 @@ export function BYOK() {
</Button>
</div>
) : (
<Button
variant='primary'
className='!bg-[var(--brand-tertiary-2)] !text-[var(--text-inverse)] hover:!bg-[var(--brand-tertiary-2)]/90'
onClick={() => openEditModal(provider.id)}
>
<Button variant='primary' onClick={() => openEditModal(provider.id)}>
Add Key
</Button>
)}
@@ -391,7 +387,7 @@ export function BYOK() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleSave}
disabled={!apiKeyInput.trim() || upsertKey.isPending}
>

View File

@@ -200,7 +200,7 @@ export function Copilot() {
setIsCreateDialogOpen(true)
setCreateError(null)
}}
variant='tertiary'
variant='primary'
disabled={isLoading}
>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
@@ -302,7 +302,7 @@ export function Copilot() {
</Button>
<Button
type='button'
variant='tertiary'
variant='primary'
onClick={handleCreateKey}
disabled={!newKeyName.trim() || generateKey.isPending}
>

View File

@@ -628,7 +628,7 @@ export function CredentialSets() {
/>
</div>
{canManageCredentialSets && (
<Button variant='tertiary' onClick={() => setShowCreateModal(true)}>
<Button variant='primary' onClick={() => setShowCreateModal(true)}>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Create
</Button>
@@ -671,7 +671,7 @@ export function CredentialSets() {
</div>
</div>
<Button
variant='tertiary'
variant='primary'
onClick={() => handleAcceptInvitation(invitation.token)}
disabled={acceptInvitation.isPending}
>
@@ -843,7 +843,7 @@ export function CredentialSets() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleCreateCredentialSet}
disabled={!newSetName.trim() || createCredentialSet.isPending}
>

View File

@@ -1311,7 +1311,7 @@ export function CredentialsManager() {
</Button>
{isSelectedAdmin && (
<Button
variant='tertiary'
variant='primary'
onClick={handleSaveDetails}
disabled={!isDetailsDirty || isSavingDetails}
>
@@ -1414,7 +1414,7 @@ export function CredentialsManager() {
<Button
onClick={handleSave}
disabled={isLoading || !hasChanges || hasConflicts || hasInvalidKeys}
variant='tertiary'
variant='primary'
className={`${hasConflicts || hasInvalidKeys ? 'cursor-not-allowed opacity-50' : ''}`}
>
Save

View File

@@ -101,7 +101,7 @@ export function CustomTools() {
className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
/>
</div>
<Button onClick={() => setShowAddForm(true)} disabled={isLoading} variant='tertiary'>
<Button onClick={() => setShowAddForm(true)} disabled={isLoading} variant='primary'>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Add
</Button>

View File

@@ -50,7 +50,7 @@ export function Debug() {
disabled={importWorkflow.isPending}
/>
<Button
variant='tertiary'
variant='primary'
onClick={handleImport}
disabled={importWorkflow.isPending || !workflowId.trim()}
>

View File

@@ -515,7 +515,7 @@ export function General() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleResetPasswordConfirm}
disabled={resetPassword.isPending || resetPassword.isSuccess}
>

View File

@@ -108,7 +108,7 @@ export function InboxEnableToggle() {
<Button variant='default' onClick={() => setIsEnableOpen(false)}>
Cancel
</Button>
<Button variant='tertiary' onClick={handleEnable} disabled={toggleInbox.isPending}>
<Button variant='primary' onClick={handleEnable} disabled={toggleInbox.isPending}>
{toggleInbox.isPending ? 'Enabling...' : 'Enable'}
</Button>
</ModalFooter>

View File

@@ -282,7 +282,7 @@ export function InboxSettingsTab() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleAddSender}
disabled={!newSenderEmail.trim() || addSender.isPending}
>

View File

@@ -41,7 +41,7 @@ export function Inbox() {
</p>
</div>
<Button
variant='tertiary'
variant='primary'
onClick={() => router.push(`/workspace/${workspaceId}/settings/subscription`)}
>
Upgrade to Max

View File

@@ -678,7 +678,7 @@ export function IntegrationsManager() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleCreateCredential}
disabled={
!createOAuthProviderId ||
@@ -1027,7 +1027,7 @@ export function IntegrationsManager() {
</Button>
{isSelectedAdmin && (
<Button
variant='tertiary'
variant='primary'
onClick={handleSaveDetails}
disabled={!isDetailsDirty || isSavingDetails}
>
@@ -1066,7 +1066,7 @@ export function IntegrationsManager() {
<Button
onClick={() => setShowCreateModal(true)}
disabled={credentialsLoading}
variant='tertiary'
variant='primary'
>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Connect

View File

@@ -719,12 +719,12 @@ export function McpServerFormModal({
<Button
onClick={handleSubmitJson}
disabled={isSubmitting || !jsonInput.trim()}
variant='tertiary'
variant='primary'
>
{isSubmitting ? 'Adding...' : submitLabel}
</Button>
) : (
<Button onClick={handleSubmitForm} disabled={isSubmitDisabled} variant='tertiary'>
<Button onClick={handleSubmitForm} disabled={isSubmitDisabled} variant='primary'>
{isSubmitting ? (mode === 'add' ? 'Adding...' : 'Saving...') : submitLabel}
</Button>
)}

View File

@@ -637,11 +637,7 @@ export function MCP({ initialServerId }: MCPProps) {
className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
/>
</div>
<Button
onClick={() => setShowAddModal(true)}
variant='tertiary'
disabled={serversLoading}
>
<Button onClick={() => setShowAddModal(true)} variant='primary' disabled={serversLoading}>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Add
</Button>

View File

@@ -303,7 +303,7 @@ export function RecentlyDeleted() {
<div className='flex shrink-0 items-center gap-[8px]'>
<span className='text-[13px] text-[var(--text-tertiary)]'>Restored</span>
<Button
variant='default'
variant='primary'
size='sm'
onClick={() =>
router.push(
@@ -316,7 +316,7 @@ export function RecentlyDeleted() {
</div>
) : (
<Button
variant='default'
variant='primary'
size='sm'
disabled={isRestoring}
onClick={() => handleRestore(resource)}

View File

@@ -210,7 +210,7 @@ export function SkillModal({
<Button variant='default' onClick={() => onOpenChange(false)}>
Cancel
</Button>
<Button variant='tertiary' onClick={handleSave} disabled={saving || !hasChanges}>
<Button variant='primary' onClick={handleSave} disabled={saving || !hasChanges}>
{saving ? 'Saving...' : initialValues ? 'Update' : 'Create'}
</Button>
</div>

View File

@@ -95,7 +95,7 @@ export function Skills() {
className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
/>
</div>
<Button onClick={() => setShowAddForm(true)} disabled={isLoading} variant='tertiary'>
<Button onClick={() => setShowAddForm(true)} disabled={isLoading} variant='primary'>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Add
</Button>

View File

@@ -160,7 +160,7 @@ export function CreditBalance({
</Button>
</ModalClose>
<Button
variant='tertiary'
variant='primary'
onClick={handlePurchase}
disabled={purchaseCredits.isPending || !amount}
>

View File

@@ -245,7 +245,7 @@ function CreditPlanCard({
{isCancelledAtPeriodEnd ? 'Restore Subscription' : 'Manage plan'}
</Button>
) : (
<Button onClick={onButtonClick} className='w-full' variant='tertiary'>
<Button onClick={onButtonClick} className='w-full' variant='primary'>
{buttonText}
</Button>
)}
@@ -1134,7 +1134,7 @@ function TeamPlanModal({ open, onOpenChange, isAnnual, onConfirm }: TeamPlanModa
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={() => onConfirm(selectedTier, selectedSeats)}
disabled={selectedSeats < 1}
>
@@ -1291,7 +1291,7 @@ function ManagePlanModal({
</span>
</div>
<Button
variant='tertiary'
variant='primary'
className='ml-[12px] shrink-0'
onClick={action.onClick}
disabled={action.disabled}
@@ -1312,7 +1312,7 @@ function ManagePlanModal({
<Button variant='default' onClick={() => onOpenChange(false)}>
Close
</Button>
<Button variant='tertiary' onClick={onRestore}>
<Button variant='primary' onClick={onRestore}>
Restore Subscription
</Button>
</>

View File

@@ -239,7 +239,7 @@ export function MemberInvitationCard({
</DropdownMenuContent>
</DropdownMenu>
<Button
variant='tertiary'
variant='primary'
onClick={() => onInviteMember()}
disabled={!hasValidEmails || isInviting || !hasAvailableSeats}
>

View File

@@ -111,7 +111,7 @@ export function NoOrganizationView({
)}
<div className='flex justify-end'>
<Button
variant='tertiary'
variant='primary'
onClick={onCreateOrganization}
disabled={!orgName || !orgSlug || isCreatingOrg}
>
@@ -193,7 +193,7 @@ export function NoOrganizationView({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={onCreateOrganization}
disabled={isCreatingOrg || !orgName.trim()}
>
@@ -217,7 +217,7 @@ export function NoOrganizationView({
</div>
<div>
<Button variant='tertiary' onClick={() => navigateToSettings({ section: 'subscription' })}>
<Button variant='primary' onClick={() => navigateToSettings({ section: 'subscription' })}>
Upgrade to Team Plan
</Button>
</div>

View File

@@ -72,7 +72,7 @@ export function TeamSeatsOverview({
</p>
</div>
<Button
variant='tertiary'
variant='primary'
onClick={() => {
onConfirmTeamUpgrade(2)
}}

View File

@@ -135,7 +135,7 @@ export function TeamSeats({
<Tooltip.Trigger asChild>
<span>
<Button
variant='tertiary'
variant='primary'
onClick={() => onConfirm(selectedSeats)}
disabled={
isLoading ||

View File

@@ -505,7 +505,7 @@ export function TemplateProfile() {
<Button
onClick={handleSubmit}
disabled={saveStatus === 'saving' || !isFormValid}
variant='tertiary'
variant='primary'
>
{saveStatus === 'saving' ? 'Saving...' : saveStatus === 'saved' ? 'Saved' : 'Save'}
</Button>

View File

@@ -349,7 +349,7 @@ function ServerDetailView({ workspaceId, serverId, onBack }: ServerDetailViewPro
<Tooltip.Trigger asChild>
<div className='inline-flex'>
<Button
variant='tertiary'
variant='primary'
onClick={() => setShowAddWorkflow(true)}
disabled
>
@@ -364,7 +364,7 @@ function ServerDetailView({ workspaceId, serverId, onBack }: ServerDetailViewPro
</Tooltip.Root>
) : (
<Button
variant='tertiary'
variant='primary'
onClick={() => setShowAddWorkflow(true)}
disabled={!canAddWorkflow}
>
@@ -480,7 +480,7 @@ function ServerDetailView({ workspaceId, serverId, onBack }: ServerDetailViewPro
workflows via the MCP block.
</p>
<Button
variant='tertiary'
variant='primary'
className='self-start'
disabled={addToWorkspaceMutation.isPending || addedToWorkspace}
onClick={async () => {
@@ -739,7 +739,7 @@ function ServerDetailView({ workspaceId, serverId, onBack }: ServerDetailViewPro
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={async () => {
if (!toolToView) return
try {
@@ -859,7 +859,7 @@ function ServerDetailView({ workspaceId, serverId, onBack }: ServerDetailViewPro
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleAddWorkflow}
disabled={!selectedWorkflowId || addToolMutation.isPending}
>
@@ -920,7 +920,7 @@ function ServerDetailView({ workspaceId, serverId, onBack }: ServerDetailViewPro
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleSaveServerEdit}
disabled={
!editServerName.trim() ||
@@ -1059,7 +1059,7 @@ export function WorkflowMcpServers() {
className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
/>
</div>
<Button onClick={() => setShowAddModal(true)} disabled={isLoading} variant='tertiary'>
<Button onClick={() => setShowAddModal(true)} disabled={isLoading} variant='primary'>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Add
</Button>
@@ -1203,7 +1203,7 @@ export function WorkflowMcpServers() {
<Button
onClick={handleCreateServer}
disabled={!isFormValid || createServerMutation.isPending}
variant='tertiary'
variant='primary'
>
{createServerMutation.isPending ? 'Adding...' : 'Add Server'}
</Button>

View File

@@ -221,7 +221,7 @@ export function RowModal({ mode, isOpen, onClose, table, row, rowIds, onSuccess
</Button>
<Button
type='button'
variant='tertiary'
variant='primary'
onClick={handleFormSubmit}
disabled={isSubmitting}
className='min-w-[120px]'

View File

@@ -65,7 +65,7 @@ export function CheckpointConfirmation({
{!isRestoreVariant && onContinue && (
<Button
onClick={onContinue}
variant='tertiary'
variant='primary'
size='sm'
className='flex-1'
disabled={isProcessing}

View File

@@ -1390,7 +1390,7 @@ function RunSkipButtons({
// Standardized buttons for all interrupt tools: Allow, Always Allow, Skip
return (
<div className='mt-[10px] flex gap-[6px]'>
<Button onClick={onRun} disabled={isProcessing} variant='tertiary'>
<Button onClick={onRun} disabled={isProcessing} variant='primary'>
{isProcessing ? 'Allowing...' : 'Allow'}
</Button>
{showAlwaysAllow && (
@@ -2130,7 +2130,7 @@ export function ToolCall({
onStateChange?.('background')
await sendToolDecision(toolCall.id, 'background')
}}
variant='tertiary'
variant='primary'
title='Move to Background'
>
Move to Background
@@ -2144,7 +2144,7 @@ export function ToolCall({
onStateChange?.('background')
await sendToolDecision(toolCall.id, 'background')
}}
variant='tertiary'
variant='primary'
title='Wake'
>
Wake

View File

@@ -1006,12 +1006,7 @@ function GeneralFooter({
<ModalFooter className='items-center justify-between'>
<StatusBadge isWarning={needsRedeployment} />
<div className='flex items-center gap-2'>
<Button
variant='default'
onClick={onUndeploy}
disabled={isUndeploying || isSubmitting}
className='px-[7px] py-[5px]'
>
<Button variant='default' onClick={onUndeploy} disabled={isUndeploying || isSubmitting}>
{isUndeploying ? 'Undeploying...' : 'Undeploy'}
</Button>
{needsRedeployment && (

View File

@@ -180,7 +180,7 @@ export function OAuthRequiredModal({
<Button variant='default' onClick={onClose}>
Cancel
</Button>
<Button variant='tertiary' type='button' onClick={handleConnectDirectly}>
<Button variant='primary' type='button' onClick={handleConnectDirectly}>
Connect
</Button>
</ModalFooter>

View File

@@ -863,7 +863,7 @@ try {
placeholder='Generate...'
/>
<Button
variant='tertiary'
variant='primary'
disabled={!schemaPromptInput.trim() || schemaGeneration.isStreaming}
onMouseDown={(e) => {
e.preventDefault()
@@ -955,7 +955,7 @@ try {
placeholder='Generate...'
/>
<Button
variant='tertiary'
variant='primary'
disabled={!codePromptInput.trim() || codeGeneration.isStreaming}
onMouseDown={(e) => {
e.preventDefault()
@@ -1135,7 +1135,7 @@ try {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={() => setActiveSection('code')}
disabled={!isSchemaValid || !!schemaError}
>
@@ -1161,7 +1161,7 @@ try {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleSave}
disabled={!isSchemaValid || !!schemaError || !hasChanges}
>

View File

@@ -129,7 +129,7 @@ export function ParameterWithLabel({
placeholder='Generate with AI...'
/>
<Button
variant='tertiary'
variant='primary'
disabled={!searchQuery.trim() || isStreaming}
onMouseDown={(e: React.MouseEvent) => {
e.preventDefault()

View File

@@ -331,7 +331,7 @@ const renderLabel = (
placeholder='Generate with AI...'
/>
<Button
variant='tertiary'
variant='primary'
disabled={!wandState.searchQuery.trim() || wandState.isStreaming}
onMouseDown={(e: React.MouseEvent) => {
e.preventDefault()

View File

@@ -372,7 +372,7 @@ export function TrainingModal() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={() => {
useCopilotTrainingStore.getState().stopTraining()
setLocalPrompt('')
@@ -439,7 +439,7 @@ export function TrainingModal() {
<Button
onClick={handleStart}
disabled={!localTitle.trim() || !localPrompt.trim()}
variant='tertiary'
variant='primary'
className='w-full'
>
Start Training Session
@@ -470,7 +470,7 @@ export function TrainingModal() {
<div className='flex gap-[8px]'>
{selectedDatasets.size > 0 && (
<Button
variant='tertiary'
variant='primary'
onClick={handleSendSelected}
disabled={sendingSelected}
>
@@ -755,7 +755,7 @@ export function TrainingModal() {
sendingLiveWorkflow ||
currentWorkflow.getBlockCount() === 0
}
variant='tertiary'
variant='primary'
className={cn(
'w-full',
liveWorkflowSent && '!bg-green-600 !text-white hover:!bg-green-700',

View File

@@ -539,7 +539,7 @@ export function HelpModal({ open, onOpenChange, workflowId, workspaceId }: HelpM
<Button variant='default' onClick={handleClose} type='button' disabled={isSubmitting}>
Cancel
</Button>
<Button type='submit' variant='tertiary' disabled={isSubmitting || isProcessing}>
<Button type='submit' variant='primary' disabled={isSubmitting || isProcessing}>
{isSubmitting
? 'Submitting...'
: submitStatus === 'error'

View File

@@ -209,7 +209,7 @@ function ColorPickerSubmenu({
className='h-[20px] min-w-0 flex-1 rounded-[4px] border border-[var(--border-1)] bg-[var(--surface-5)] px-[6px] font-medium text-[11px] text-[var(--text-primary)] uppercase transition-colors focus:outline-none focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0'
/>
<Button
variant='tertiary'
variant='primary'
disabled={!canSubmitHex}
onClick={(e) => {
e.stopPropagation()

View File

@@ -77,7 +77,7 @@ export function CreateWorkspaceModal({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={() => void handleSubmit()}
disabled={!name.trim() || isCreating}
>

View File

@@ -575,7 +575,7 @@ export function InviteModal({ open, onOpenChange, workspaceName }: InviteModalPr
</Button>
<Button
type='button'
variant='tertiary'
variant='primary'
disabled={isSaving || isSubmitting}
onClick={handleSaveChanges}
tabIndex={hasPendingChanges && userPerms.canAdmin ? 0 : -1}
@@ -586,7 +586,7 @@ export function InviteModal({ open, onOpenChange, workspaceName }: InviteModalPr
<Button
type='button'
variant='tertiary'
variant='primary'
onClick={() => formRef.current?.requestSubmit()}
disabled={
!userPerms.canAdmin || isSubmitting || isSaving || !workspaceId || !hasNewInvites

View File

@@ -365,6 +365,7 @@ export function WorkspaceHeader({
align='start'
side={isCollapsed ? 'right' : 'bottom'}
sideOffset={isCollapsed ? 16 : 8}
className='flex max-h-none flex-col overflow-hidden'
style={
isCollapsed
? {
@@ -385,7 +386,7 @@ export function WorkspaceHeader({
</div>
) : (
<>
<div className='flex items-center gap-[8px] px-[2px] py-[4px]'>
<div className='flex items-center gap-[8px] px-[2px] py-[2px]'>
<div
className='flex h-[32px] w-[32px] flex-shrink-0 items-center justify-center rounded-[6px] font-medium text-[12px] text-white'
style={{
@@ -404,7 +405,7 @@ export function WorkspaceHeader({
</div>
</div>
<DropdownMenuGroup className='mt-[4px]'>
<DropdownMenuGroup className='mt-[4px] min-h-0 flex-1'>
<div className='flex max-h-[130px] flex-col gap-[2px] overflow-y-auto'>
{workspaces.map((workspace) => (
<div key={workspace.id}>
@@ -492,7 +493,9 @@ export function WorkspaceHeader({
</div>
))}
</div>
</DropdownMenuGroup>
<div className='flex flex-col gap-[2px]'>
<button
type='button'
className='flex w-full cursor-pointer select-none items-center gap-[8px] rounded-[5px] px-[8px] py-[5px] font-medium text-[12px] text-[var(--text-body)] outline-none transition-colors hover:bg-[var(--surface-active)] disabled:pointer-events-none disabled:opacity-50'
@@ -506,7 +509,7 @@ export function WorkspaceHeader({
<Plus className='h-[14px] w-[14px] shrink-0 text-[var(--text-icon)]' />
Create new workspace
</button>
</DropdownMenuGroup>
</div>
{!isInvitationsDisabled && (
<>

View File

@@ -1056,10 +1056,7 @@ export const Sidebar = memo(function Sidebar() {
<div className='flex flex-shrink-0 flex-col space-y-[4px] px-[16px]'>
<div className='flex items-center justify-between'>
<div
className={cn(
'font-base text-[var(--text-icon)] text-small',
isCollapsed && 'opacity-0'
)}
className={`font-base text-[var(--text-icon)] text-small${isCollapsed ? ' opacity-0' : ''}`}
>
All tasks
</div>

View File

@@ -18,5 +18,9 @@ export default function WorkspaceRootLayout({ children }: WorkspaceRootLayoutPro
}
: undefined
return <SocketProvider user={user}>{children}</SocketProvider>
return (
<SocketProvider user={user}>
<div className='tracking-[0.02em]'>{children}</div>
</SocketProvider>
)
}

View File

@@ -20,7 +20,7 @@ export const colors = {
/** Brand primary - purple */
brandPrimary: '#6f3dfa',
/** Brand tertiary - green (matches Run/Deploy buttons) */
brandTertiary: '#32bd7e',
brandTertiary: '#33C482',
/** Border/divider color */
divider: '#ededed',
/** Footer background */

View File

@@ -104,7 +104,7 @@ const buttonGroupItemVariants = cva(
{
variants: {
active: {
true: 'bg-[var(--brand-tertiary-2)] text-[var(--text-inverse)] border-[var(--brand-tertiary-2)] hover:brightness-106',
true: 'bg-[#1D1D1D] text-[var(--text-inverse)] border-[#1D1D1D] hover:bg-[#2A2A2A] hover:border-[#2A2A2A] dark:bg-white dark:border-white dark:hover:bg-[#E0E0E0] dark:hover:border-[#E0E0E0]',
false:
'bg-[var(--surface-4)] text-[var(--text-secondary)] border-[var(--border)] hover:text-[var(--text-primary)] hover:bg-[var(--surface-6)] hover:border-[var(--border-1)]',
},

View File

@@ -14,11 +14,12 @@ const buttonVariants = cva(
'3d': 'text-[var(--text-tertiary)] border-t border-l border-r border-[var(--border-1)] shadow-[0_2px_0_0_var(--border-1)] hover:shadow-[0_4px_0_0_var(--border-1)] transition-all hover:-translate-y-0.5 hover:text-[var(--text-primary)]',
outline:
'border border-[var(--text-muted)] bg-transparent hover:border-[var(--text-secondary)]',
primary: 'bg-[var(--brand-400)] text-[var(--text-primary)] hover:brightness-106',
primary:
'bg-[#1D1D1D] text-[var(--text-inverse)] hover:text-[var(--text-inverse)] hover:bg-[#2A2A2A] dark:bg-white dark:hover:bg-[#E0E0E0]',
destructive: 'bg-[var(--text-error)] text-white hover:text-white hover:brightness-106',
secondary: 'bg-[var(--brand-secondary)] text-[var(--text-primary)]',
tertiary:
'!bg-[var(--brand-tertiary-2)] !text-[var(--text-inverse)] hover:brightness-106 hover:!text-[var(--text-inverse)] ![transition-property:background-color,border-color,fill,stroke]',
'!bg-[var(--brand-tertiary-2)] !text-[var(--text-inverse)] hover:!text-[var(--text-inverse)] hover:!bg-[#2DAC72] dark:!bg-[var(--brand-tertiary-2)] dark:hover:!bg-[#2DAC72] dark:!text-[var(--text-inverse)] dark:hover:!text-[var(--text-inverse)]',
ghost: '',
subtle: 'text-[var(--text-body)] hover:text-[var(--text-body)] hover:bg-[var(--surface-4)]',
'ghost-secondary': 'text-[var(--text-muted)]',

View File

@@ -70,7 +70,7 @@ export function DiffControlsDemo() {
height: '100%',
alignItems: 'center',
border: '1px solid rgba(0, 0, 0, 0.15)',
backgroundColor: '#32bd7e',
backgroundColor: '#33C482',
paddingRight: '12px',
paddingLeft: '20px',
fontWeight: 500,

View File

@@ -138,7 +138,7 @@ function AddMembersModal({
className='h-auto flex-1 border-0 bg-transparent p-0 font-base text-[14px] leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
/>
</div>
<Button variant='tertiary' onClick={handleToggleAll}>
<Button variant='primary' onClick={handleToggleAll}>
{allFilteredSelected ? 'Deselect All' : 'Select All'}
</Button>
</div>
@@ -203,7 +203,7 @@ function AddMembersModal({
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={onAddMembers}
disabled={selectedMemberIds.size === 0 || isAdding}
>
@@ -724,7 +724,7 @@ export function AccessControl() {
<span className='font-medium text-[14px] text-[var(--text-secondary)]'>
Members
</span>
<Button variant='tertiary' onClick={handleOpenAddMembersModal}>
<Button variant='primary' onClick={handleOpenAddMembersModal}>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Add
</Button>
@@ -842,7 +842,7 @@ export function AccessControl() {
/>
</div>
<Button
variant='tertiary'
variant='primary'
onClick={() => {
const allAllowed =
editingConfig?.allowedModelProviders === null ||
@@ -896,7 +896,7 @@ export function AccessControl() {
/>
</div>
<Button
variant='tertiary'
variant='primary'
onClick={() => {
const allAllowed =
editingConfig?.allowedIntegrations === null ||
@@ -957,7 +957,7 @@ export function AccessControl() {
/>
</div>
<Button
variant='tertiary'
variant='primary'
onClick={() => {
const allVisible =
!editingConfig?.hideKnowledgeBaseTab &&
@@ -1080,7 +1080,7 @@ export function AccessControl() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleSaveConfig}
disabled={updatePermissionGroup.isPending || !hasConfigChanges}
>
@@ -1113,7 +1113,7 @@ export function AccessControl() {
Discard Changes
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={() => {
setShowUnsavedChanges(false)
handleSaveConfig()
@@ -1155,7 +1155,7 @@ export function AccessControl() {
className='h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
/>
</div>
<Button variant='tertiary' onClick={() => setShowCreateModal(true)}>
<Button variant='primary' onClick={() => setShowCreateModal(true)}>
<Plus className='mr-[6px] h-[13px] w-[13px]' />
Create
</Button>
@@ -1248,7 +1248,7 @@ export function AccessControl() {
Cancel
</Button>
<Button
variant='tertiary'
variant='primary'
onClick={handleCreatePermissionGroup}
disabled={!newGroupName.trim() || createPermissionGroup.isPending}
>

View File

@@ -446,7 +446,7 @@ export function SSO() {
{/* Footer */}
<div className='mt-auto flex items-center justify-end'>
<Button onClick={handleEdit} variant='tertiary'>
<Button onClick={handleEdit} variant='primary'>
Edit
</Button>
</div>
@@ -879,7 +879,7 @@ export function SSO() {
{error && <p className='mr-auto text-[13px] text-[var(--text-error)]'>{error}</p>}
<Button
type='submit'
variant='tertiary'
variant='primary'
disabled={configureSSOMutation.isPending || hasAnyErrors(errors) || !isFormValid()}
>
{configureSSOMutation.isPending

View File

@@ -1047,7 +1047,7 @@ export const invitation = pgTable(
export const workspace = pgTable('workspace', {
id: text('id').primaryKey(),
name: text('name').notNull(),
color: text('color').notNull().default('#32bd7e'),
color: text('color').notNull().default('#33C482'),
ownerId: text('owner_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),