From 3e6c870764ff4971341446e3591db2d5be9d9ba8 Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Tue, 22 Sep 2026 12:54:12 -0700 Subject: [PATCH 1/5] improvement(ui): share workflow preview actions --- .../workflow-sidebar/workflow-sidebar.tsx | 35 +++++++---------- .../panel/components/editor/editor.tsx | 22 ++++------- .../preview-editor/preview-editor.tsx | 38 +++++++------------ .../workflow/workflow-preview-action.tsx | 34 +++++++++++++++++ 4 files changed, 67 insertions(+), 62 deletions(-) create mode 100644 apps/sim/components/workflow/workflow-preview-action.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx index e213befae6c..62c96b80f78 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/workflow-sidebar/workflow-sidebar.tsx @@ -13,12 +13,12 @@ import { Loader, OverflowText, Switch, - Tooltip, toast, } from '@sim/emcn' import { ArrowLeft, SquareArrowUpRight, X } from '@sim/emcn/icons' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' +import { WorkflowPreviewAction } from '@/components/workflow/workflow-preview-action' import { findValidationIssue, isValidationError } from '@/lib/api/client/errors' import type { AddWorkflowGroupBodyInput, @@ -711,27 +711,18 @@ export function WorkflowSidebarBody({ /> {!isEnrichment && ( - - - - - Open workflow - + + window.open( + `/workspace/${workspaceId}/w/${selectedWorkflowId}`, + '_blank', + 'noopener,noreferrer' + ) + } + > + + )} ) : ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx index 61bd02da17b..9222584ba9e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx @@ -24,6 +24,7 @@ import { useParams } from 'next/navigation' import { usePostHog } from 'posthog-js/react' import { useShallow } from 'zustand/react/shallow' import { useStoreWithEqualityFn } from 'zustand/traditional' +import { WorkflowPreviewAction } from '@/components/workflow/workflow-preview-action' import { isMcpRuntimeReference } from '@/lib/mcp/operation-policy' import { resolveMcpBlockConfig } from '@/lib/mcp/workflow-config' import { captureEvent } from '@/lib/posthog/client' @@ -610,21 +611,12 @@ export function Editor() { lightweight /> - - - - - Open workflow - + + + ) : (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx index b51a54f5277..8b027c4c74a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx @@ -30,6 +30,7 @@ import { import { formatDuration } from '@sim/utils/formatting' import { ReactFlowProvider } from '@xyflow/react' import { useParams } from 'next/navigation' +import { WorkflowPreviewAction } from '@/components/workflow/workflow-preview-action' import { extractReferencePrefixes } from '@/lib/workflows/sanitization/references' import { buildCanonicalIndexForSurface, @@ -1380,31 +1381,18 @@ function PreviewEditorContent({ cursorStyle='grab' />
- - - - - - {isExecutionMode && onDrillDown ? 'Expand workflow' : 'Open in new tab'} - - + + {isExecutionMode && onDrillDown ? ( + + ) : ( + + )} + ) : (
diff --git a/apps/sim/components/workflow/workflow-preview-action.tsx b/apps/sim/components/workflow/workflow-preview-action.tsx new file mode 100644 index 00000000000..e372a7bf4e3 --- /dev/null +++ b/apps/sim/components/workflow/workflow-preview-action.tsx @@ -0,0 +1,34 @@ +'use client' + +import { type ComponentProps, forwardRef } from 'react' +import { Button, Tooltip } from '@sim/emcn' + +interface WorkflowPreviewActionProps + extends Omit< + ComponentProps, + 'variant' | 'size' | 'iconSize' | 'iconPadding' | 'type' | 'className' + > { + 'aria-label': string +} + +/** Solid corner action shared by embedded workflow previews. */ +export const WorkflowPreviewAction = forwardRef( + ({ 'aria-label': label, ...props }, ref) => ( + + +
@@ -533,7 +533,7 @@ console.log(limits);` code={getStreamCommand()} language={LANGUAGE_SYNTAX[language]} wrapText - className='min-h-0! rounded-sm border border-[var(--border-1)]' + className='min-h-0!' /> @@ -579,7 +579,7 @@ console.log(limits);` code={getAsyncCommand()} language={LANGUAGE_SYNTAX[language]} wrapText - className='min-h-0! rounded-sm border border-[var(--border-1)]' + className='min-h-0!' /> )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-panel/output-panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-panel/output-panel.tsx index 7eb787be542..daa24590d8e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-panel/output-panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-panel/output-panel.tsx @@ -66,7 +66,8 @@ const OutputCodeContent = React.memo(function OutputCodeContent({ code={code} showGutter language={language} - className='m-0 min-h-full rounded-none border-0 bg-[var(--bg)] dark:bg-[var(--bg)]' + appearance='flat' + className='m-0 min-h-full' paddingLeft={8} gutterStyle={{ backgroundColor: 'transparent' }} wrapText={wrapText} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx index 8b027c4c74a..715532c9643 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx @@ -1226,7 +1226,8 @@ function PreviewEditorContent({ { ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true + vi.stubGlobal( + 'ResizeObserver', + class { + observe() {} + unobserve() {} + disconnect() {} + } + ) host = document.createElement('div') document.body.appendChild(host) root = createRoot(host) @@ -20,6 +28,7 @@ beforeEach(() => { afterEach(() => { if (root) act(() => root?.unmount()) host?.remove() + vi.unstubAllGlobals() root = null host = null }) @@ -60,3 +69,79 @@ describe('Code.Viewer workflow references', () => { expect(host?.querySelector('[data-search-match]')?.textContent).toBe('result') }) }) + +describe('Code.Viewer appearances', () => { + for (const virtualized of [false, true]) { + it(`applies the inspection surface to ${virtualized ? 'virtualized' : 'standard'} output`, async () => { + await act(async () => { + root?.render( + + ) + await sleep(1) + }) + + const viewer = host?.firstElementChild + expect(viewer?.classList.contains('rounded-md')).toBe(true) + expect(viewer?.classList.contains('border-0')).toBe(true) + expect(viewer?.classList.contains('bg-[var(--surface-4)]!')).toBe(true) + expect(viewer?.classList.contains('dark:bg-[var(--surface-3)]!')).toBe(true) + expect(viewer?.classList.contains('max-h-[300px]')).toBe(true) + }) + } + + it('keeps the flat viewer separate from the default code container', async () => { + await act(async () => { + root?.render( + <> + + + + ) + await sleep(1) + }) + + const [defaultViewer, flatViewer] = Array.from(host?.children ?? []) + expect(defaultViewer.classList.contains('rounded-sm')).toBe(true) + expect(flatViewer.classList.contains('rounded-none')).toBe(true) + expect(flatViewer.classList.contains('bg-[var(--bg)]')).toBe(true) + expect(flatViewer.classList.contains('dark:bg-[var(--bg)]')).toBe(true) + expect(flatViewer.textContent).toContain('flat') + }) + + it('applies flat chrome on the virtualized gutter path used by the terminal', async () => { + const contentRef = createRef() + await act(async () => { + root?.render( + + ) + await sleep(1) + }) + + const viewer = host?.firstElementChild + expect(contentRef.current).toBe(viewer) + expect(viewer?.classList.contains('rounded-none')).toBe(true) + expect(viewer?.classList.contains('border-0')).toBe(true) + expect(viewer?.classList.contains('bg-[var(--bg)]')).toBe(true) + expect(viewer?.classList.contains('dark:bg-[var(--bg)]')).toBe(true) + expect(viewer?.classList.contains('overflow-x-hidden')).toBe(true) + expect(viewer?.classList.contains('min-h-full')).toBe(true) + expect(viewer?.classList.contains('rounded-sm')).toBe(false) + }) +}) diff --git a/packages/emcn/src/components/code/code.tsx b/packages/emcn/src/components/code/code.tsx index 2e540301caa..2f0a42a5781 100644 --- a/packages/emcn/src/components/code/code.tsx +++ b/packages/emcn/src/components/code/code.tsx @@ -13,6 +13,7 @@ import { import { escapeRegExp } from '@sim/utils/string' import { findWorkflowReferenceTokens } from '@sim/utils/workflow-references' import { useVirtualizer } from '@tanstack/react-virtual' +import { cva, type VariantProps } from 'class-variance-authority' import { ChevronRight } from '../../icons' import { cn } from '../../lib/cn' import './code.css' @@ -856,6 +857,18 @@ function applySearchHighlightingToLine( */ type CodeViewerDensity = 'default' | 'compact' +/** Container appearances shared by the standard and virtualized viewers. */ +export const codeViewerAppearanceVariants = cva('', { + variants: { + appearance: { + default: '', + inspection: 'rounded-md border-0 bg-[var(--surface-4)]! dark:bg-[var(--surface-3)]!', + flat: 'rounded-none border-0 bg-[var(--bg)] dark:bg-[var(--bg)]', + }, + }, + defaultVariants: { appearance: 'default' }, +}) + interface CodeViewerProps { /** Code content to display */ code: string @@ -865,6 +878,8 @@ interface CodeViewerProps { language?: 'javascript' | 'json' | 'python' | 'bash' | 'toml' /** Additional CSS classes for the container */ className?: string + /** Container appearance for code inspected in logs/previews or on flat surfaces. */ + appearance?: NonNullable['appearance']> /** Visual density for read-only code. */ density?: CodeViewerDensity /** Highlight Sim `{{ENV}}` and `` references with the platform accent. */ @@ -948,6 +963,7 @@ type ViewerInnerProps = { language: 'javascript' | 'json' | 'python' | 'bash' | 'toml' /** Additional CSS classes for the container */ className?: string + appearance: NonNullable /** Visual density for read-only code. */ density: CodeViewerDensity highlightWorkflowReferences: boolean @@ -978,6 +994,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({ showGutter, language, className, + appearance, density, highlightWorkflowReferences, paddingLeft, @@ -1147,6 +1164,7 @@ const VirtualizedViewerInner = memo(function VirtualizedViewerInner({ wrapText ? 'overflow-x-hidden' : 'overflow-x-auto', 'overflow-y-auto', 'dark:bg-[var(--code-bg)]', + codeViewerAppearanceVariants({ appearance }), className )} style={{ height: containerHeight }} @@ -1196,6 +1214,7 @@ const ViewerInner = memo(function ViewerInner({ showGutter, language, className, + appearance, density, highlightWorkflowReferences, paddingLeft, @@ -1309,7 +1328,7 @@ const ViewerInner = memo(function ViewerInner({ // Grid-based rendering for gutter alignment (works with wrap) if (showGutter) { return ( - +
+
       
     

From 5ca5a291dcc410b3c4e677812ccc2ad028a46322 Mon Sep 17 00:00:00 2001
From: Bill Leoutsakos <157128530+BillLeoutsakosvl346@users.noreply.github.com>
Date: Thu, 24 Sep 2026 15:09:59 -0700
Subject: [PATCH 4/5] Share table sidebar shell and scroll body (#8249)

* Share table sidebar shell and scroll body

* test(tables): cover shared sidebar dialog state

* test(tables): preserve sidebar scroll state

---------

Co-authored-by: Bill Leoutsakos 
---
 .../column-config-sidebar.tsx                 | 20 +++---
 .../enrichments-sidebar/enrichment-config.tsx |  5 +-
 .../enrichments-sidebar.tsx                   | 14 ++--
 .../components/table-sidebar-layout.test.tsx  | 65 +++++++++++++++++++
 .../components/table-sidebar-layout.tsx       | 41 ++++++++++++
 .../workflow-sidebar/workflow-sidebar.tsx     | 20 +++---
 6 files changed, 129 insertions(+), 36 deletions(-)
 create mode 100644 apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-layout.test.tsx
 create mode 100644 apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-layout.tsx

diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx
index 2ad4ebc773b..a1db5e3daf5 100644
--- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx
@@ -5,7 +5,6 @@ import {
   Button,
   ChipCombobox,
   ChipInput,
-  cn,
   FieldDivider,
   Label,
   Switch,
@@ -29,6 +28,10 @@ import {
   TableSidebarHeader,
   TableSidebarHeaderAction,
 } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-header/table-sidebar-header'
+import {
+  TableSidebarScrollBody,
+  TableSidebarShell,
+} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-sidebar-layout'
 import { useAddTableColumn, useUpdateColumn } from '@/hooks/queries/tables'
 import { SelectOptionsEditor } from '../select-field'
 import { columnTypeOptionsForTable } from './column-types'
@@ -100,18 +103,11 @@ export function ColumnConfigSidebar(props: ColumnConfigSidebarProps) {
   // different column / mode remounts and re-seeds state from props.
   const open = props.config !== null
   return (
-    
+    
   )
 }
 
@@ -274,7 +270,7 @@ function ColumnConfigBody({
         
       
 
-      
+ {/* `disabled` on the fieldset reaches every native control inside, including the comboboxes' trigger buttons; `contents` keeps the existing layout. Values stay readable and selectable. */} @@ -383,7 +379,7 @@ function ColumnConfigBody({ )} -
+
@@ -770,12 +771,13 @@ export function FileUpload({ iconSize='compact' className='-translate-y-1/2 absolute top-1/2 right-[4px]' onClick={(e) => handleRemoveFile(file, e)} - disabled={isDeleting} + disabled={disabled || isPreview || isDeleting} + data-preview-full-opacity={isPreview || undefined} > {isDeleting ? (
) : ( - + )}
@@ -972,7 +974,7 @@ export function FileUpload({ onOpenChange={(open) => { if (open) void refetchWorkspaceFiles() }} - disabled={disabled} + disabled={disabled || isPreview} isLoading={loadingWorkspaceFiles} formatFileSize={formatFileSize} truncateMiddle={truncateMiddle} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx index d385a77ab71..b503433c4af 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx @@ -123,7 +123,7 @@ export function GroupedCheckboxList({ )} > - + Configure PII Types
(
+
{ if (el) descriptionOverlayRefs.current[field.id] = el }} + data-preview-full-opacity={isPreview || undefined} style={{ scrollbarWidth: 'none' }} className={cn( 'pointer-events-none absolute inset-0 flex items-center overflow-x-auto bg-transparent px-2 py-1.5 font-sans text-sm', diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx index 54b7d751123..adecacf7d6a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx @@ -1634,6 +1634,7 @@ export const ToolInput = memo(function ToolInput({ onDrop={(e) => handleDrop(e, toolIndex)} >
{renderLabel( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx index 715532c9643..bba86b895e8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-editor.tsx @@ -43,6 +43,7 @@ import { import { DELETED_WORKFLOW_LABEL } from '@/lib/workflows/workflow-labels' import { SubBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components' import { PreviewContextMenu } from '@/app/workspace/[workspaceId]/w/components/preview/components/preview-context-menu' +import { READONLY_PREVIEW_STYLES } from '@/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-readonly-styles' import { PreviewWorkflow } from '@/app/workspace/[workspaceId]/w/components/preview/components/preview-workflow' import { getBlock } from '@/blocks' import { BlockTile } from '@/blocks/block-tile' @@ -54,31 +55,6 @@ import { useCodeViewerFeatures } from '@/hooks/use-code-viewer' import { useContextMenu } from '@/hooks/use-context-menu' import type { BlockState, Loop, Parallel, WorkflowState } from '@/stores/workflows/workflow/types' -/** - * CSS override to show full opacity and prevent interaction in readonly preview mode. - * Extracted to avoid duplicating the style block in multiple places. - */ -const READONLY_PREVIEW_STYLES = ` - .readonly-preview, - .readonly-preview * { - cursor: default !important; - } - .readonly-preview [disabled], - .readonly-preview [data-disabled], - .readonly-preview input, - .readonly-preview textarea, - .readonly-preview [role="combobox"], - .readonly-preview [role="slider"], - .readonly-preview [role="switch"], - .readonly-preview [role="checkbox"] { - opacity: 1 !important; - pointer-events: none; - } - .readonly-preview .opacity-50 { - opacity: 1 !important; - } -` - /** * Format a value for display as JSON string */ @@ -606,7 +582,7 @@ function SubflowConfigDisplay({ block, loop, parallel }: SubflowConfigDisplayPro return (
{/* Type Selection - matches SubflowEditor */} -
+
@@ -629,7 +605,7 @@ function SubflowConfigDisplay({ block, loop, parallel }: SubflowConfigDisplayPro {isCountMode ? ( -
+
{index < visibleSubBlocks.length - 1 && ( ({ useParams: () => ({ workspaceId: 'workspace-1' }) })) +vi.mock('@/hooks/use-webhook-management', () => ({ + useWebhookManagement: () => ({ webhookUrl: null }), +})) +vi.mock( + '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value', + () => ({ useSubBlockValue: () => [undefined, vi.fn()] }) +) + +import { SubBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block' +import { READONLY_PREVIEW_STYLES } from '@/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-readonly-styles' +import type { SubBlockConfig } from '@/blocks/types' + +const config: SubBlockConfig = { id: 'enabled', type: 'switch', title: 'Enabled' } + +let container: HTMLDivElement +let root: Root + +beforeEach(() => { + ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true + container = document.createElement('div') + document.body.appendChild(container) + root = createRoot(container) +}) + +afterEach(() => { + act(() => root.unmount()) + container.remove() +}) + +describe('workflow preview read-only appearance', () => { + it('shows a preview value at full opacity while retaining the disabled interaction state', () => { + act(() => + root.render( + <> + +
+
+ +
+ +
+
+
+ +
+
+ + ) + ) + + const preview = container.querySelector('[data-testid="preview"]')! + const previewSwitch = preview.querySelector('[role="switch"]') as HTMLButtonElement + const disabled = container.querySelector('[data-testid="disabled"]')! + const disabledSwitch = disabled.querySelector('[role="switch"]') as HTMLButtonElement + + expect(preview.querySelector('[data-preview-readonly]')).not.toBeNull() + expect(previewSwitch.hasAttribute('disabled')).toBe(true) + expect(previewSwitch.getAttribute('aria-checked')).toBe('true') + expect(getComputedStyle(previewSwitch).opacity).toBe('1') + expect(getComputedStyle(previewSwitch).pointerEvents).toBe('none') + const removeButton = preview.querySelector('button:not([role="switch"])') as HTMLButtonElement + expect(getComputedStyle(removeButton).pointerEvents).toBe('none') + expect(getComputedStyle(removeButton).opacity).toBe('0.5') + + act(() => { + previewSwitch.click() + previewSwitch.dispatchEvent(new KeyboardEvent('keydown', { key: ' ', bubbles: true })) + }) + expect(previewSwitch.getAttribute('aria-checked')).toBe('true') + + expect(disabled.querySelector('[data-preview-readonly]')).toBeNull() + expect(disabledSwitch.hasAttribute('disabled')).toBe(true) + expect(getComputedStyle(disabledSwitch).opacity).toBe('0.5') + }) +}) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-readonly-styles.ts b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-readonly-styles.ts new file mode 100644 index 00000000000..3fe8107450f --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-editor/preview-readonly-styles.ts @@ -0,0 +1,27 @@ +/** + * Keep preview fields noninteractive while showing intentionally read-only controls + * and their marked value overlays at full opacity in both preview sections. + */ +export const READONLY_PREVIEW_STYLES = ` + .readonly-preview, + .readonly-preview * { + cursor: default !important; + } + .readonly-preview [data-preview-readonly] :is( + input, + textarea, + [role="combobox"], + [role="slider"], + [role="switch"], + [role="checkbox"] + ) { + opacity: 1 !important; + pointer-events: none; + } + .readonly-preview [data-preview-readonly] :is(button, [role="button"]) { + pointer-events: none; + } + .readonly-preview [data-preview-readonly] [data-preview-full-opacity] { + opacity: 1 !important; + } +`