Skip to content

Commit 03c23d5

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Add shared Code.Viewer appearances for product surfaces
1 parent 319f8bb commit 03c23d5

10 files changed

Lines changed: 96 additions & 13 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,8 @@ function DetailCodeSection({
495495
<Code.Viewer
496496
code={jsonString}
497497
language='json'
498-
className='max-w-full rounded-md border-0 bg-[var(--surface-4)]! [word-break:break-all] dark:bg-[var(--surface-3)]!'
498+
appearance='inspection'
499+
className='max-w-full [word-break:break-all]'
499500
wrapText
500501
searchQuery={isSearchActive ? searchQuery : undefined}
501502
currentMatchIndex={currentMatchIndex}

‎apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ export const WorkflowOutputSection = memo(
159159
<Code.Viewer
160160
code={jsonString}
161161
language='json'
162-
className='max-h-[300px] min-h-0 max-w-full rounded-md border-0 bg-[var(--surface-4)]! [word-break:break-all] dark:bg-[var(--surface-3)]!'
162+
appearance='inspection'
163+
className='max-h-[300px] min-h-0 max-w-full [word-break:break-all]'
163164
wrapText
164165
searchQuery={isSearchActive ? searchQuery : undefined}
165166
currentMatchIndex={currentMatchIndex}

‎apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ function ServerDetailView({
607607
: 'json'
608608
}
609609
wrapText
610-
className='min-h-0! rounded-sm border border-[var(--border-1)]'
610+
className='min-h-0!'
611611
/>
612612
{activeConfigTab === 'cursor' && (
613613
<a

‎apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ console.log(limits);`
493493
code={getSyncCommand()}
494494
language={LANGUAGE_SYNTAX[language]}
495495
wrapText
496-
className='min-h-0! rounded-sm border border-[var(--border-1)]'
496+
className='min-h-0!'
497497
/>
498498
</div>
499499

@@ -533,7 +533,7 @@ console.log(limits);`
533533
code={getStreamCommand()}
534534
language={LANGUAGE_SYNTAX[language]}
535535
wrapText
536-
className='min-h-0! rounded-sm border border-[var(--border-1)]'
536+
className='min-h-0!'
537537
/>
538538
</div>
539539

@@ -579,7 +579,7 @@ console.log(limits);`
579579
code={getAsyncCommand()}
580580
language={LANGUAGE_SYNTAX[language]}
581581
wrapText
582-
className='min-h-0! rounded-sm border border-[var(--border-1)]'
582+
className='min-h-0!'
583583
/>
584584
</div>
585585
)}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ const OutputCodeContent = React.memo(function OutputCodeContent({
6666
code={code}
6767
showGutter
6868
language={language}
69-
className='m-0 min-h-full rounded-none border-0 bg-[var(--bg)] dark:bg-[var(--bg)]'
69+
appearance='flat'
70+
className='m-0 min-h-full'
7071
paddingLeft={8}
7172
gutterStyle={{ backgroundColor: 'transparent' }}
7273
wrapText={wrapText}

‎apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,8 @@ function PreviewEditorContent({
12261226
<Code.Viewer
12271227
code={formatValueAsJson(executionData.input)}
12281228
language='json'
1229-
className='max-h-[300px] min-h-0 max-w-full rounded-md border-0 bg-[var(--surface-4)]! [word-break:break-all] dark:bg-[var(--surface-3)]!'
1229+
appearance='inspection'
1230+
className='max-h-[300px] min-h-0 max-w-full [word-break:break-all]'
12301231
wrapText={wrapText}
12311232
searchQuery={isSearchActive ? searchQuery : undefined}
12321233
currentMatchIndex={currentMatchIndex}
@@ -1293,8 +1294,9 @@ function PreviewEditorContent({
12931294
<Code.Viewer
12941295
code={formatValueAsJson(executionData.output)}
12951296
language='json'
1297+
appearance='inspection'
12961298
className={cn(
1297-
'max-h-[300px] min-h-0 max-w-full rounded-md border-0 bg-[var(--surface-4)]! [word-break:break-all] dark:bg-[var(--surface-3)]!',
1299+
'max-h-[300px] min-h-0 max-w-full [word-break:break-all]',
12981300
executionData.status === 'error' && 'text-[var(--text-error)]'
12991301
)}
13001302
wrapText={wrapText}

‎packages/emcn/src/components/code/code.test.tsx‎

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@
44
import { act } from 'react'
55
import { sleep } from '@sim/utils/helpers'
66
import { createRoot, type Root } from 'react-dom/client'
7-
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
7+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
88
import { Code } from './code'
99

1010
let root: Root | null = null
1111
let host: HTMLDivElement | null = null
1212

1313
beforeEach(() => {
1414
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
15+
vi.stubGlobal(
16+
'ResizeObserver',
17+
class {
18+
observe() {}
19+
unobserve() {}
20+
disconnect() {}
21+
}
22+
)
1523
host = document.createElement('div')
1624
document.body.appendChild(host)
1725
root = createRoot(host)
@@ -20,6 +28,7 @@ beforeEach(() => {
2028
afterEach(() => {
2129
if (root) act(() => root?.unmount())
2230
host?.remove()
31+
vi.unstubAllGlobals()
2332
root = null
2433
host = null
2534
})
@@ -60,3 +69,49 @@ describe('Code.Viewer workflow references', () => {
6069
expect(host?.querySelector('[data-search-match]')?.textContent).toBe('result')
6170
})
6271
})
72+
73+
describe('Code.Viewer appearances', () => {
74+
for (const virtualized of [false, true]) {
75+
it(`applies the inspection surface to ${virtualized ? 'virtualized' : 'standard'} output`, async () => {
76+
await act(async () => {
77+
root?.render(
78+
<Code.Viewer
79+
code='{"result": true}'
80+
language='json'
81+
showGutter
82+
virtualized={virtualized}
83+
appearance='inspection'
84+
className='max-h-[300px]'
85+
/>
86+
)
87+
await sleep(1)
88+
})
89+
90+
const viewer = host?.firstElementChild
91+
expect(viewer?.classList.contains('rounded-md')).toBe(true)
92+
expect(viewer?.classList.contains('border-0')).toBe(true)
93+
expect(viewer?.classList.contains('bg-[var(--surface-4)]!')).toBe(true)
94+
expect(viewer?.classList.contains('dark:bg-[var(--surface-3)]!')).toBe(true)
95+
expect(viewer?.classList.contains('max-h-[300px]')).toBe(true)
96+
})
97+
}
98+
99+
it('keeps the flat viewer separate from the default code container', async () => {
100+
await act(async () => {
101+
root?.render(
102+
<>
103+
<Code.Viewer code='default' />
104+
<Code.Viewer code='flat' appearance='flat' />
105+
</>
106+
)
107+
await sleep(1)
108+
})
109+
110+
const [defaultViewer, flatViewer] = Array.from(host?.children ?? [])
111+
expect(defaultViewer.classList.contains('rounded-sm')).toBe(true)
112+
expect(flatViewer.classList.contains('rounded-none')).toBe(true)
113+
expect(flatViewer.classList.contains('bg-[var(--bg)]')).toBe(true)
114+
expect(flatViewer.classList.contains('dark:bg-[var(--bg)]')).toBe(true)
115+
expect(flatViewer.textContent).toContain('flat')
116+
})
117+
})

‎packages/emcn/src/components/code/code.tsx‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { escapeRegExp } from '@sim/utils/string'
1414
import { findWorkflowReferenceTokens } from '@sim/utils/workflow-references'
1515
import { useVirtualizer } from '@tanstack/react-virtual'
16+
import { cva, type VariantProps } from 'class-variance-authority'
1617
import { ChevronRight } from '../../icons'
1718
import { cn } from '../../lib/cn'
1819
import './code.css'
@@ -856,6 +857,18 @@ function applySearchHighlightingToLine(
856857
*/
857858
type CodeViewerDensity = 'default' | 'compact'
858859

860+
/** Container appearances shared by the standard and virtualized viewers. */
861+
export const codeViewerAppearanceVariants = cva('', {
862+
variants: {
863+
appearance: {
864+
default: '',
865+
inspection: 'rounded-md border-0 bg-[var(--surface-4)]! dark:bg-[var(--surface-3)]!',
866+
flat: 'rounded-none border-0 bg-[var(--bg)] dark:bg-[var(--bg)]',
867+
},
868+
},
869+
defaultVariants: { appearance: 'default' },
870+
})
871+
859872
interface CodeViewerProps {
860873
/** Code content to display */
861874
code: string
@@ -865,6 +878,8 @@ interface CodeViewerProps {
865878
language?: 'javascript' | 'json' | 'python' | 'bash' | 'toml'
866879
/** Additional CSS classes for the container */
867880
className?: string
881+
/** Container appearance for code inspected in logs/previews or on flat surfaces. */
882+
appearance?: NonNullable<VariantProps<typeof codeViewerAppearanceVariants>['appearance']>
868883
/** Visual density for read-only code. */
869884
density?: CodeViewerDensity
870885
/** Highlight Sim `{{ENV}}` and `<block.output>` references with the platform accent. */
@@ -948,6 +963,7 @@ type ViewerInnerProps = {
948963
language: 'javascript' | 'json' | 'python' | 'bash' | 'toml'
949964
/** Additional CSS classes for the container */
950965
className?: string
966+
appearance: NonNullable<CodeViewerProps['appearance']>
951967
/** Visual density for read-only code. */
952968
density: CodeViewerDensity
953969
highlightWorkflowReferences: boolean
@@ -978,6 +994,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
978994
showGutter,
979995
language,
980996
className,
997+
appearance,
981998
density,
982999
highlightWorkflowReferences,
9831000
paddingLeft,
@@ -1147,6 +1164,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({
11471164
wrapText ? 'overflow-x-hidden' : 'overflow-x-auto',
11481165
'overflow-y-auto',
11491166
'dark:bg-[var(--code-bg)]',
1167+
codeViewerAppearanceVariants({ appearance }),
11501168
className
11511169
)}
11521170
style={{ height: containerHeight }}
@@ -1196,6 +1214,7 @@ const ViewerInner = memo(function ViewerInner({
11961214
showGutter,
11971215
language,
11981216
className,
1217+
appearance,
11991218
density,
12001219
highlightWorkflowReferences,
12011220
paddingLeft,
@@ -1309,7 +1328,7 @@ const ViewerInner = memo(function ViewerInner({
13091328
// Grid-based rendering for gutter alignment (works with wrap)
13101329
if (showGutter) {
13111330
return (
1312-
<Container className={className}>
1331+
<Container className={cn(codeViewerAppearanceVariants({ appearance }), className)}>
13131332
<Content className='code-editor-theme' editorRef={contentRef}>
13141333
<div
13151334
style={{
@@ -1370,7 +1389,7 @@ const ViewerInner = memo(function ViewerInner({
13701389

13711390
// Simple display without gutter
13721391
return (
1373-
<Container className={className}>
1392+
<Container className={cn(codeViewerAppearanceVariants({ appearance }), className)}>
13741393
<Content className='code-editor-theme' editorRef={contentRef}>
13751394
<pre
13761395
className={cn(
@@ -1417,6 +1436,7 @@ function Viewer({
14171436
showGutter = false,
14181437
language = 'json',
14191438
className,
1439+
appearance = 'default',
14201440
density = 'default',
14211441
highlightWorkflowReferences = false,
14221442
paddingLeft = 0,
@@ -1434,6 +1454,7 @@ function Viewer({
14341454
showGutter,
14351455
language,
14361456
className,
1457+
appearance,
14371458
density,
14381459
highlightWorkflowReferences,
14391460
paddingLeft,

‎packages/emcn/src/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export {
1515
CODE_LINE_HEIGHT_PX,
1616
Code,
1717
calculateGutterWidth,
18+
codeViewerAppearanceVariants,
1819
getCodeEditorProps,
1920
} from './components/code/code'
2021
export {

‎packages/workflow-renderer/src/lib/code-hover-card.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ export function CodeHoverCard({ preview, className, children }: CodeHoverCardPro
181181
density='compact'
182182
paddingLeft={8}
183183
highlightWorkflowReferences
184-
className='max-h-[min(16rem,calc(100vh-2rem))] min-h-0 overflow-x-hidden rounded-none border-0 bg-[var(--bg)] shadow-none dark:bg-[var(--bg)]'
184+
appearance='flat'
185+
className='max-h-[min(16rem,calc(100vh-2rem))] min-h-0 overflow-x-hidden shadow-none'
185186
/>
186187
</PopoverContent>
187188
</Popover>

0 commit comments

Comments
 (0)