Skip to content

Commit 80eaeb0

Browse files
authored
improvement(platform): chat, emcn, terminal, usage-limit (#1974)
* improvement(usage-indicator): layout * improvement: expand default terminal height * fix: swap workflow block ports * improvement: chat initial positioning * improvement(chat): display; improvement(emcn): popover attributes
1 parent b67b4ff commit 80eaeb0

File tree

9 files changed

+91
-60
lines changed

9 files changed

+91
-60
lines changed

apps/sim/app/globals.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
--panel-width: 244px;
1212
--toolbar-triggers-height: 300px;
1313
--editor-connections-height: 200px;
14-
--terminal-height: 145px;
14+
--terminal-height: 196px;
1515
}
1616

1717
.sidebar-container {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ export function Chat() {
569569

570570
return (
571571
<div
572-
className='fixed z-30 flex flex-col overflow-hidden rounded-[6px] bg-[var(--surface-1)] px-[10px] pt-[2px] pb-[8px]'
572+
className='fixed z-30 flex flex-col overflow-hidden rounded-[6px] border border-[var(--border)] bg-[var(--surface-1)] px-[10px] pt-[2px] pb-[8px]'
573573
style={{
574574
left: `${actualPosition.x}px`,
575575
top: `${actualPosition.y}px`,
@@ -619,6 +619,7 @@ export function Chat() {
619619
side='bottom'
620620
align='end'
621621
sideOffset={8}
622+
maxHeight={100}
622623
style={{ width: '110px', minWidth: '110px' }}
623624
>
624625
<PopoverScrollArea>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -288,36 +288,41 @@ export function OutputSelect({
288288
<PopoverContent
289289
ref={popoverRef}
290290
side='bottom'
291-
align='start'
291+
align='end'
292292
sideOffset={4}
293-
maxHeight={280}
293+
maxHeight={140}
294+
maxWidth={140}
295+
minWidth={140}
294296
onOpenAutoFocus={(e) => e.preventDefault()}
295297
onCloseAutoFocus={(e) => e.preventDefault()}
296298
>
297299
<PopoverScrollArea className='space-y-[2px]'>
298300
{Object.entries(groupedOutputs).map(([blockName, outputs]) => (
299301
<div key={blockName}>
300302
<PopoverSection>{blockName}</PopoverSection>
301-
{outputs.map((output) => (
302-
<PopoverItem
303-
key={output.id}
304-
active={isSelectedValue(output)}
305-
onClick={() => handleOutputSelection(output.label)}
306-
>
307-
<div
308-
className='flex h-[14px] w-[14px] flex-shrink-0 items-center justify-center rounded'
309-
style={{
310-
backgroundColor: getOutputColor(output.blockId, output.blockType),
311-
}}
303+
304+
<div className='flex flex-col gap-[2px]'>
305+
{outputs.map((output) => (
306+
<PopoverItem
307+
key={output.id}
308+
active={isSelectedValue(output)}
309+
onClick={() => handleOutputSelection(output.label)}
312310
>
313-
<span className='font-bold text-[10px] text-white'>
314-
{blockName.charAt(0).toUpperCase()}
315-
</span>
316-
</div>
317-
<span className='min-w-0 flex-1 truncate'>{output.path}</span>
318-
{isSelectedValue(output) && <Check className='h-3 w-3 flex-shrink-0' />}
319-
</PopoverItem>
320-
))}
311+
<div
312+
className='flex h-[14px] w-[14px] flex-shrink-0 items-center justify-center rounded'
313+
style={{
314+
backgroundColor: getOutputColor(output.blockId, output.blockType),
315+
}}
316+
>
317+
<span className='font-bold text-[10px] text-white'>
318+
{blockName.charAt(0).toUpperCase()}
319+
</span>
320+
</div>
321+
<span className='min-w-0 flex-1 truncate'>{output.path}</span>
322+
{isSelectedValue(output) && <Check className='h-3 w-3 flex-shrink-0' />}
323+
</PopoverItem>
324+
))}
325+
</div>
321326
</div>
322327
))}
323328
</PopoverScrollArea>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import { useOutputPanelResize, useTerminalFilters, useTerminalResize } from './h
4242
*/
4343
const MIN_HEIGHT = 30
4444
const NEAR_MIN_THRESHOLD = 40
45-
const DEFAULT_EXPANDED_HEIGHT = 300
45+
const DEFAULT_EXPANDED_HEIGHT = 196
4646

4747
/**
4848
* Column width constants - numeric values for calculations

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
22
import { useParams } from 'next/navigation'
3-
import { Handle, type NodeProps, Position } from 'reactflow'
3+
import { Handle, type NodeProps, Position, useUpdateNodeInternals } from 'reactflow'
44
import { Badge } from '@/components/emcn/components/badge/badge'
55
import { Tooltip } from '@/components/emcn/components/tooltip/tooltip'
66
import { getEnv, isTruthy } from '@/lib/env'
@@ -689,6 +689,16 @@ export const WorkflowBlock = memo(function WorkflowBlock({
689689
],
690690
})
691691

692+
/**
693+
* Notify React Flow when handle orientation changes so it can recalculate edge paths.
694+
* This is necessary because toggling handles doesn't change block dimensions,
695+
* so useBlockDimensions won't trigger updateNodeInternals.
696+
*/
697+
const updateNodeInternals = useUpdateNodeInternals()
698+
useEffect(() => {
699+
updateNodeInternals(id)
700+
}, [horizontalHandles, id, updateNodeInternals])
701+
692702
const showWebhookIndicator = (isStarterBlock || isWebhookTriggerBlock) && isWebhookConfigured
693703
const shouldShowScheduleBadge =
694704
type === 'schedule' && !isLoadingScheduleInfo && scheduleInfo !== null

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/usage-indicator/usage-indicator.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,14 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) {
6262

6363
if (isLoading) {
6464
return (
65-
<div className='flex flex-shrink-0 flex-col gap-[10px] border-t px-[13.5px] pt-[10px] pb-[8px] dark:border-[var(--border)]'>
65+
<div className='flex flex-shrink-0 flex-col gap-[8px] border-t pt-[12px] pr-[13.5px] pb-[10px] pl-[12px] dark:border-[var(--border)]'>
6666
{/* Top row skeleton */}
6767
<div className='flex items-center justify-between'>
68-
<Skeleton className='h-[16px] w-[120px] rounded-[4px]' />
69-
<Skeleton className='h-[16px] w-[50px] rounded-[4px]' />
68+
<div className='flex items-center gap-[6px]'>
69+
<Skeleton className='h-[14px] w-[40px] rounded-[4px]' />
70+
<Skeleton className='h-[14px] w-[70px] rounded-[4px]' />
71+
</div>
72+
<Skeleton className='h-[12px] w-[50px] rounded-[4px]' />
7073
</div>
7174

7275
{/* Pills skeleton */}
@@ -121,25 +124,25 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) {
121124
}
122125

123126
return (
124-
<div className='flex flex-shrink-0 flex-col gap-[10px] border-t px-[13.5px] pt-[8px] pb-[8px] dark:border-[var(--border)]'>
127+
<div className='flex flex-shrink-0 flex-col gap-[8px] border-t px-[13.5px] pt-[8px] pb-[10px] dark:border-[var(--border)]'>
125128
{/* Top row */}
126129
<div className='flex items-center justify-between'>
127130
<div className='flex items-center gap-[6px]'>
128131
<span className='font-medium text-[#FFFFFF] text-[12px]'>{PLAN_NAMES[planType]}</span>
129-
<div className='h-[14px] w-[1.5px] bg-[#4A4A4A]' />
132+
<div className='h-[14px] w-[1.5px] bg-[var(--divider)]' />
130133
<div className='flex items-center gap-[4px]'>
131134
{isBlocked ? (
132135
<>
133-
<span className='font-medium text-[#B1B1B1] text-[12px]'>Over</span>
134-
<span className='font-medium text-[#B1B1B1] text-[12px]'>limit</span>
136+
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>Over</span>
137+
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>limit</span>
135138
</>
136139
) : (
137140
<>
138-
<span className='font-medium text-[#B1B1B1] text-[12px] tabular-nums'>
141+
<span className='font-medium text-[12px] text-[var(--text-tertiary)] tabular-nums'>
139142
${usage.current.toFixed(2)}
140143
</span>
141-
<span className='font-medium text-[#B1B1B1] text-[12px]'>/</span>
142-
<span className='font-medium text-[#B1B1B1] text-[12px] tabular-nums'>
144+
<span className='font-medium text-[12px] text-[var(--text-tertiary)]'>/</span>
145+
<span className='font-medium text-[12px] text-[var(--text-tertiary)] tabular-nums'>
143146
${usage.limit}
144147
</span>
145148
</>
@@ -149,7 +152,7 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) {
149152
{showUpgradeButton && (
150153
<Button
151154
variant='ghost'
152-
className='!h-auto !px-1 !py-0 -mx-1 mt-[-2px] text-[#D4D4D4]'
155+
className='!h-auto !px-1 !py-0 -mx-1 mt-[-2px] text-[var(--text-secondary)]'
153156
onClick={handleClick}
154157
>
155158
Upgrade

apps/sim/components/emcn/components/popover/popover.tsx

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,15 @@ export interface PopoverContentProps
205205
* Maximum height for the popover content in pixels
206206
*/
207207
maxHeight?: number
208+
/**
209+
* Maximum width for the popover content in pixels.
210+
* When provided, Popover will also enable default truncation for inner text and section headers.
211+
*/
212+
maxWidth?: number
213+
/**
214+
* Minimum width for the popover content in pixels
215+
*/
216+
minWidth?: number
208217
/**
209218
* Preferred side to display the popover
210219
* @default 'bottom'
@@ -249,6 +258,8 @@ const PopoverContent = React.forwardRef<
249258
style,
250259
children,
251260
maxHeight,
261+
maxWidth,
262+
minWidth,
252263
side = 'bottom',
253264
align = 'start',
254265
sideOffset,
@@ -264,7 +275,11 @@ const PopoverContent = React.forwardRef<
264275
// When present, we enable default text truncation behavior for inner flexible items,
265276
// so callers don't need to manually pass 'truncate' to every label.
266277
const hasUserWidthConstraint =
267-
style?.minWidth !== undefined || style?.maxWidth !== undefined || style?.width !== undefined
278+
maxWidth !== undefined ||
279+
minWidth !== undefined ||
280+
style?.minWidth !== undefined ||
281+
style?.maxWidth !== undefined ||
282+
style?.width !== undefined
268283

269284
return (
270285
<PopoverPrimitive.Portal>
@@ -278,15 +293,21 @@ const PopoverContent = React.forwardRef<
278293
sticky='partial'
279294
{...restProps}
280295
className={cn(
281-
'z-[10000001] flex flex-col overflow-hidden rounded-[8px] bg-[var(--surface-3)] px-[5.5px] py-[5px] text-foreground outline-none dark:bg-[var(--surface-3)]',
282-
// If width is constrained by the caller, ensure inner flexible text truncates by default.
283-
hasUserWidthConstraint && '[&_.flex-1]:truncate',
284-
className
296+
'z-[10000001] flex flex-col overflow-auto rounded-[8px] bg-[var(--surface-3)] px-[5.5px] py-[5px] text-foreground outline-none dark:bg-[var(--surface-3)]',
297+
// If width is constrained by the caller (prop or style), ensure inner flexible text truncates by default,
298+
// and also truncate section headers.
299+
hasUserWidthConstraint && '[&_.flex-1]:truncate [&_[data-popover-section]]:truncate',
285300
)}
286301
style={{
287302
maxHeight: `${maxHeight || 400}px`,
288-
maxWidth: 'calc(100vw - 16px)',
289-
minWidth: '160px',
303+
maxWidth: maxWidth !== undefined ? `${maxWidth}px` : 'calc(100vw - 16px)',
304+
// Only enforce default min width when the user hasn't set width constraints
305+
minWidth:
306+
minWidth !== undefined
307+
? `${minWidth}px`
308+
: hasUserWidthConstraint
309+
? undefined
310+
: '160px',
290311
...style,
291312
}}
292313
>
@@ -319,7 +340,7 @@ const PopoverScrollArea = React.forwardRef<HTMLDivElement, PopoverScrollAreaProp
319340
({ className, ...props }, ref) => {
320341
return (
321342
<div
322-
className={cn('min-h-0 flex-1 overflow-auto overscroll-contain', className)}
343+
className={cn('min-h-0 overflow-auto overscroll-contain', className)}
323344
ref={ref}
324345
{...props}
325346
/>
@@ -415,9 +436,10 @@ const PopoverSection = React.forwardRef<HTMLDivElement, PopoverSectionProps>(
415436
return (
416437
<div
417438
className={cn(
418-
'px-[6px] py-[4px] font-base text-[12px] text-[var(--text-tertiary)] dark:text-[var(--text-tertiary)]',
439+
'min-w-0 px-[6px] py-[4px] font-base text-[12px] text-[var(--text-tertiary)] dark:text-[var(--text-tertiary)]',
419440
className
420441
)}
442+
data-popover-section=''
421443
ref={ref}
422444
{...props}
423445
/>

apps/sim/stores/chat/store.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,31 +109,21 @@ interface ChatState {
109109
}
110110

111111
/**
112-
* Calculate default center position based on available canvas space
112+
* Calculate default position in top right of canvas, 32px from top and right of panel
113113
*/
114114
const calculateDefaultPosition = (): ChatPosition => {
115115
if (typeof window === 'undefined') {
116116
return { x: 100, y: 100 }
117117
}
118118

119119
// Get current layout dimensions
120-
const sidebarWidth = Number.parseInt(
121-
getComputedStyle(document.documentElement).getPropertyValue('--sidebar-width') || '0'
122-
)
123120
const panelWidth = Number.parseInt(
124121
getComputedStyle(document.documentElement).getPropertyValue('--panel-width') || '0'
125122
)
126-
const terminalHeight = Number.parseInt(
127-
getComputedStyle(document.documentElement).getPropertyValue('--terminal-height') || '0'
128-
)
129-
130-
// Calculate available space
131-
const availableWidth = window.innerWidth - sidebarWidth - panelWidth
132-
const availableHeight = window.innerHeight - terminalHeight
133123

134-
// Center in available space
135-
const x = sidebarWidth + (availableWidth - DEFAULT_WIDTH) / 2
136-
const y = (availableHeight - DEFAULT_HEIGHT) / 2
124+
// Position in top right of canvas, 32px from top and 32px from right of panel
125+
const x = window.innerWidth - panelWidth - 32 - DEFAULT_WIDTH
126+
const y = 32
137127

138128
return { x, y }
139129
}

apps/sim/stores/terminal/store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ interface TerminalState {
2525
* Note: Maximum height is enforced dynamically at 70% of viewport height in the resize hook
2626
*/
2727
const MIN_TERMINAL_HEIGHT = 30
28-
export const DEFAULT_TERMINAL_HEIGHT = 145
28+
export const DEFAULT_TERMINAL_HEIGHT = 196
2929

3030
/**
3131
* Output panel width constraints

0 commit comments

Comments
 (0)