Skip to content

Commit 18bcb2b

Browse files
committed
refactor(emcn): finish the cn migration by dropping clsx
apps/sim still imported clsx directly in 8 files, so the repo carried two class-name paths — one that resolves Tailwind conflicts and one that does not. Converts them all to `cn` and removes the dependency. clsx joins; cn merges. The two differ only where a call emits conflicting classes, so every call site was checked rather than assumed. Of 19 sites with 2+ class tokens, 6 could conflict: - 4 already resolve the same way, because cn keeps the class CSS source order was picking anyway (cursor-grab/pointer, opacity-0/100, text-secondary/tertiary) - 2 are mutually exclusive ternaries in output-panel; only one branch ships The seventh is a latent bug rather than a merge difference: workflow-item's overlay sets `pointer-events-none` in its base and `pointer-events-auto` when the context menu is open, but Tailwind emits `pointer-events-none` last, so it always won — that button has never been clickable while the menu is open, only visible. Left rendering as-is with a comment; enabling it is a real fix but not a rendering-neutral one. Verified all four runtime branches of that call still yield `pointer-events-none`, and the built stylesheet is byte-identical.
1 parent 266de32 commit 18bcb2b

10 files changed

Lines changed: 42 additions & 40 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/components/field-item/field-item.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
'use client'
22

33
import { useCallback } from 'react'
4-
import { Badge, handleKeyboardActivation } from '@sim/emcn'
4+
import { Badge, cn, handleKeyboardActivation } from '@sim/emcn'
55
import { ChevronDown } from '@sim/emcn/icons'
66
import { createLogger } from '@sim/logger'
7-
import clsx from 'clsx'
87
import type { ConnectedBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/hooks/use-block-connections'
98
import { normalizeName } from '@/executor/constants'
109

@@ -83,13 +82,13 @@ export function FieldItem({
8382
if (!hasChildren) return
8483
handleKeyboardActivation(event, handleClick)
8584
}}
86-
className={clsx(
85+
className={cn(
8786
'group flex h-[26px] cursor-grab items-center gap-2 rounded-lg px-1.5 text-sm hover-hover:bg-[var(--surface-6)] active:cursor-grabbing dark:hover-hover:bg-[var(--surface-5)]',
8887
hasChildren && 'cursor-pointer'
8988
)}
9089
>
9190
<span
92-
className={clsx(
91+
className={cn(
9392
'min-w-0 flex-1 truncate',
9493
'text-[var(--text-secondary)] group-hover:text-[var(--text-primary)]'
9594
)}
@@ -99,7 +98,7 @@ export function FieldItem({
9998
<Badge className='shrink-0 rounded-sm px-1.5 py-[1px] font-mono text-xs'>{field.type}</Badge>
10099
{hasChildren && (
101100
<ChevronDown
102-
className={clsx(
101+
className={cn(
103102
'size-[14px] shrink-0 transition-transform duration-100',
104103
'text-[var(--text-secondary)] group-hover:text-[var(--text-primary)]',
105104
isExpanded && 'rotate-180'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
'use client'
22

33
import { useCallback, useRef, useState } from 'react'
4-
import { ChevronDown, handleKeyboardActivation } from '@sim/emcn'
4+
import { ChevronDown, cn, handleKeyboardActivation } from '@sim/emcn'
55
import { createLogger } from '@sim/logger'
6-
import clsx from 'clsx'
76
import { useShallow } from 'zustand/react/shallow'
87
import {
98
FieldItem,
@@ -113,7 +112,7 @@ function ConnectionItem({
113112
tabIndex={hasFields ? 0 : undefined}
114113
draggable
115114
onDragStart={(e) => onConnectionDragStart(e, connection)}
116-
className={clsx(
115+
className={cn(
117116
'group flex h-[26px] cursor-grab items-center gap-2 rounded-lg px-1.5 text-sm hover-hover:bg-[var(--surface-6)] active:cursor-grabbing dark:hover-hover:bg-[var(--surface-5)]',
118117
hasFields && 'cursor-pointer'
119118
)}
@@ -125,7 +124,7 @@ function ConnectionItem({
125124
>
126125
<BlockTile blockType={connection.type} size='sm' />
127126
<span
128-
className={clsx(
127+
className={cn(
129128
'truncate',
130129
'text-[var(--text-secondary)] group-hover:text-[var(--text-primary)]'
131130
)}
@@ -134,7 +133,7 @@ function ConnectionItem({
134133
</span>
135134
{hasFields && (
136135
<ChevronDown
137-
className={clsx(
136+
className={cn(
138137
'size-[8px] shrink-0 text-[var(--text-tertiary)] transition-transform duration-100 group-hover:text-[var(--text-primary)]',
139138
!isExpanded && '-rotate-90'
140139
)}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/toolbar/toolbar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {
2222
OverflowText,
2323
} from '@sim/emcn'
2424
import { ChevronDown, Search } from '@sim/emcn/icons'
25-
import clsx from 'clsx'
2625
import { useParams } from 'next/navigation'
2726
import { usePostHog } from 'posthog-js/react'
2827
import { captureEvent } from '@/lib/posthog/client'
@@ -319,7 +318,7 @@ const ToolbarSection = memo(function ToolbarSection({
319318
>
320319
<span className='text-[var(--text-muted)] text-small'>{label}</span>
321320
<ChevronDown
322-
className={clsx(
321+
className={cn(
323322
'size-[14px] text-[var(--text-icon)] transition-transform duration-150',
324323
!expanded && '-rotate-90'
325324
)}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-panel/output-panel.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
44
import {
55
Button,
66
Code,
7+
cn,
78
Input,
89
Popover,
910
PopoverContent,
@@ -23,7 +24,6 @@ import {
2324
Trash,
2425
X,
2526
} from '@sim/emcn/icons'
26-
import clsx from 'clsx'
2727
import Link from 'next/link'
2828
import {
2929
AgentStreamThinkingChrome,
@@ -313,7 +313,7 @@ export const OutputPanel = React.memo(function OutputPanel({
313313
<div className='flex items-center'>
314314
<Button
315315
variant='ghost'
316-
className={clsx(
316+
className={cn(
317317
'px-2 py-1.5 text-small',
318318
!showInput ? 'text-[var(--text-primary)]!' : 'text-[var(--text-icon)]!'
319319
)}
@@ -325,7 +325,7 @@ export const OutputPanel = React.memo(function OutputPanel({
325325
{hasInputData && (
326326
<Button
327327
variant='ghost'
328-
className={clsx(
328+
className={cn(
329329
'px-2 py-1.5 text-small',
330330
showInput ? 'text-[var(--text-primary)]!' : 'text-[var(--text-icon)]!'
331331
)}
@@ -503,7 +503,7 @@ export const OutputPanel = React.memo(function OutputPanel({
503503
className='mr-0.5 h-[23px] w-[94px] text-caption'
504504
/>
505505
<span
506-
className={clsx(
506+
className={cn(
507507
'w-[58px] text-xs',
508508
matchCount > 0 ? 'text-[var(--text-secondary)]' : 'text-[var(--text-tertiary)]'
509509
)}
@@ -541,7 +541,7 @@ export const OutputPanel = React.memo(function OutputPanel({
541541

542542
{/* Content */}
543543
<div
544-
className={clsx('flex-1 overflow-y-auto', !wrapText && 'overflow-x-auto')}
544+
className={cn('flex-1 overflow-y-auto', !wrapText && 'overflow-x-auto')}
545545
onContextMenu={handleOutputPanelContextMenu}
546546
>
547547
{!showInput &&

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/toggle-button/toggle-button.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
import type React from 'react'
44
import { memo } from 'react'
5-
import { Button } from '@sim/emcn'
5+
import { Button, cn } from '@sim/emcn'
66
import { ChevronDown } from '@sim/emcn/icons'
7-
import clsx from 'clsx'
87

98
export interface ToggleButtonProps {
109
isExpanded: boolean
@@ -23,7 +22,7 @@ export const ToggleButton = memo(function ToggleButton({ isExpanded, onClick }:
2322
aria-label='Toggle terminal'
2423
>
2524
<ChevronDown
26-
className={clsx(
25+
className={cn(
2726
'size-[14px] shrink-0 transition-transform duration-100',
2827
!isExpanded && 'rotate-180'
2928
)}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
55
import {
66
Button,
77
ChevronDown,
8+
cn,
89
disclosureChevronClass,
910
handleKeyboardActivation,
1011
Popover,
@@ -16,7 +17,6 @@ import {
1617
import { ArrowDown, ArrowUp, Download, MoreHorizontal, Palette, Trash } from '@sim/emcn/icons'
1718
import { formatDuration } from '@sim/utils/formatting'
1819
import { useVirtualizer } from '@tanstack/react-virtual'
19-
import clsx from 'clsx'
2020
import Link from 'next/link'
2121
import { getEnv, isTruthy } from '@/lib/core/config/env'
2222
import { sendMothershipMessage } from '@/lib/mothership/events'
@@ -129,7 +129,7 @@ const BlockRow = memo(function BlockRow({
129129
{entry.blockName}
130130
</span>
131131
</div>
132-
<span className={clsx(ROW_STYLES.status, !isRunning && ROW_STYLES.statusIdle)}>
132+
<span className={cn(ROW_STYLES.status, !isRunning && ROW_STYLES.statusIdle)}>
133133
<StatusDisplay
134134
isRunning={isRunning}
135135
isCanceled={isCanceled}
@@ -190,10 +190,10 @@ const IterationNodeRow = memo(function IterationNodeRow({
190190
{iterationLabel}
191191
</span>
192192
{hasChildren && (
193-
<ChevronDown className={clsx(disclosureChevronClass, !isExpanded && '-rotate-90')} />
193+
<ChevronDown className={cn(disclosureChevronClass, !isExpanded && '-rotate-90')} />
194194
)}
195195
</div>
196-
<span className={clsx(ROW_STYLES.status, !hasRunningChild && ROW_STYLES.statusIdle)}>
196+
<span className={cn(ROW_STYLES.status, !hasRunningChild && ROW_STYLES.statusIdle)}>
197197
<StatusDisplay
198198
isRunning={hasRunningChild}
199199
isCanceled={hasCanceledChild}
@@ -274,10 +274,10 @@ const SubflowNodeRow = memo(function SubflowNodeRow({
274274
<EntryBlockTile blockType={entry.blockType} />
275275
<span className={hasError ? ROW_STYLES.labelError : ROW_STYLES.label}>{displayName}</span>
276276
{hasChildren && (
277-
<ChevronDown className={clsx(disclosureChevronClass, !isExpanded && '-rotate-90')} />
277+
<ChevronDown className={cn(disclosureChevronClass, !isExpanded && '-rotate-90')} />
278278
)}
279279
</div>
280-
<span className={clsx(ROW_STYLES.status, !hasRunningDescendant && ROW_STYLES.statusIdle)}>
280+
<span className={cn(ROW_STYLES.status, !hasRunningDescendant && ROW_STYLES.statusIdle)}>
281281
<StatusDisplay
282282
isRunning={hasRunningDescendant}
283283
isCanceled={hasCanceledDescendant}
@@ -373,10 +373,10 @@ const WorkflowNodeRow = memo(function WorkflowNodeRow({
373373
{entry.blockName}
374374
</span>
375375
{hasChildren && (
376-
<ChevronDown className={clsx(disclosureChevronClass, !isExpanded && '-rotate-90')} />
376+
<ChevronDown className={cn(disclosureChevronClass, !isExpanded && '-rotate-90')} />
377377
)}
378378
</div>
379-
<span className={clsx(ROW_STYLES.status, !hasRunningDescendant && ROW_STYLES.statusIdle)}>
379+
<span className={cn(ROW_STYLES.status, !hasRunningDescendant && ROW_STYLES.statusIdle)}>
380380
<StatusDisplay
381381
isRunning={hasRunningDescendant}
382382
isCanceled={hasCanceledDescendant}
@@ -1201,7 +1201,7 @@ export const Terminal = memo(function Terminal() {
12011201
<>
12021202
<aside
12031203
ref={terminalRef}
1204-
className={clsx(
1204+
className={cn(
12051205
'terminal-container relative shrink-0 overflow-hidden border-[var(--border)] border-t bg-[var(--bg)]',
12061206
isToggling && 'transition-[height] duration-100 ease-out'
12071207
)}
@@ -1223,7 +1223,7 @@ export const Terminal = memo(function Terminal() {
12231223
<div className='relative flex h-full'>
12241224
{/* Left Section - Logs */}
12251225
<div
1226-
className={clsx('flex flex-col', !selectedEntry && 'flex-1')}
1226+
className={cn('flex flex-col', !selectedEntry && 'flex-1')}
12271227
style={selectedEntry ? { width: 'calc(100% - var(--output-panel-width))' } : undefined}
12281228
>
12291229
{/* Header */}

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { memo, useCallback, useMemo, useRef, useState } from 'react'
44
import { chipVariants, cn, OverflowText } from '@sim/emcn'
55
import { Lock, MoreHorizontal } from '@sim/emcn/icons'
6-
import clsx from 'clsx'
76
import Link from 'next/link'
87
import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types'
98
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
@@ -454,7 +453,7 @@ export const WorkflowItem = memo(function WorkflowItem({
454453
<span
455454
role='img'
456455
aria-label='Workflow is locked'
457-
className={clsx(
456+
className={cn(
458457
'pointer-events-none absolute inset-0 flex items-center justify-center transition-opacity',
459458
!isAnyDragActive && 'group-hover:opacity-0',
460459
isContextMenuOpen && 'opacity-0'
@@ -468,10 +467,19 @@ export const WorkflowItem = memo(function WorkflowItem({
468467
aria-label='Workflow options'
469468
onPointerDown={handleMorePointerDown}
470469
onClick={handleMoreClick}
471-
className={clsx(
470+
className={cn(
472471
'pointer-events-none absolute inset-0 flex items-center justify-center rounded-sm opacity-0 transition-opacity',
473472
!isAnyDragActive && 'group-hover:pointer-events-auto group-hover:opacity-100',
474-
isContextMenuOpen && 'pointer-events-auto opacity-100'
473+
/**
474+
* `opacity-100` only. `pointer-events-auto` belongs here too,
475+
* but it has never applied: under `clsx` both it and the base
476+
* `pointer-events-none` shipped, and Tailwind emits
477+
* `pointer-events-none` last, so it won. Adding it back under
478+
* `cn` (where the last argument wins) would make the button
479+
* clickable for the first time — a real fix, but a behaviour
480+
* change that does not belong in a rendering-neutral upgrade.
481+
*/
482+
isContextMenuOpen && 'opacity-100'
475483
)}
476484
>
477485
<MoreHorizontal className='size-[16px] text-[var(--text-icon)]' />

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/workflow-list.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
4-
import clsx from 'clsx'
4+
import { cn } from '@sim/emcn'
55
import { useShallow } from 'zustand/react/shallow'
66
import { buildFolderTree, getFolderPath } from '@/lib/folders/tree'
77
import { EmptyAreaContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/empty-area-context-menu'
@@ -445,7 +445,7 @@ export const WorkflowList = memo(function WorkflowList({
445445
<div key={folder.id} className='relative'>
446446
<DropIndicatorLine show={showBefore} level={level} position='before' />
447447
<div
448-
className={clsx(
448+
className={cn(
449449
'pointer-events-none absolute inset-0 z-10 rounded-sm',
450450
showInside && isDragging ? 'bg-[var(--text-subtle)] opacity-10' : 'hidden'
451451
)}
@@ -570,12 +570,12 @@ export const WorkflowList = memo(function WorkflowList({
570570
data-empty-area
571571
>
572572
<div
573-
className={clsx('relative flex-1 rounded-sm', !hasRootItems && 'min-h-[26px]')}
573+
className={cn('relative flex-1 rounded-sm', !hasRootItems && 'min-h-[26px]')}
574574
{...rootDropZoneHandlers}
575575
data-empty-area
576576
>
577577
<div
578-
className={clsx(
578+
className={cn(
579579
'pointer-events-none absolute inset-0 z-10 rounded-sm',
580580
showRootInside && isDragging ? 'bg-[var(--text-subtle)] opacity-10' : 'hidden'
581581
)}

apps/sim/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@
162162
"busboy": "1.6.0",
163163
"cheerio": "1.1.2",
164164
"class-variance-authority": "^0.7.1",
165-
"clsx": "^2.1.1",
166165
"cmdk": "^1.0.0",
167166
"croner": "^9.0.0",
168167
"cronstrue": "3.3.0",

bun.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)