From a8d6e87020e490ff32015ae7b9406d9bfcf458c9 Mon Sep 17 00:00:00 2001 From: Bill Leoutsakos Date: Sat, 19 Sep 2026 12:05:07 -0700 Subject: [PATCH] refactor(ui): reuse EMCN chips for AI controls --- .claude/rules/sim-imports.md | 2 +- .cursor/rules/sim-imports.mdc | 2 +- .../sub-block/components/code/code.test.tsx | 33 +++++- .../sub-block/components/code/code.tsx | 30 ++--- .../components/long-input/long-input.test.tsx | 13 ++- .../components/long-input/long-input.tsx | 13 +-- .../components/short-input/short-input.tsx | 12 +- .../wand-prompt-bar/wand-button.tsx | 27 +++++ .../wand-prompt-bar/wand-prompt-bar.test.tsx | 103 ++++++++++++++++++ .../wand-prompt-bar/wand-prompt-bar.tsx | 24 ++-- apps/sim/components/ui/button.tsx | 49 --------- apps/sim/components/ui/index.ts | 1 - 12 files changed, 190 insertions(+), 119 deletions(-) create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-button.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar.test.tsx delete mode 100644 apps/sim/components/ui/button.tsx diff --git a/.claude/rules/sim-imports.md b/.claude/rules/sim-imports.md index 3aeafa0dd2e..6757ecc1d43 100644 --- a/.claude/rules/sim-imports.md +++ b/.claude/rules/sim-imports.md @@ -13,8 +13,8 @@ paths: ```typescript // ✓ Good +import { Chip } from '@sim/emcn' import { useWorkflowStore } from '@/stores/workflows/store' -import { Button } from '@/components/ui/button' // ✗ Bad import { useWorkflowStore } from '../../../stores/workflows/store' diff --git a/.cursor/rules/sim-imports.mdc b/.cursor/rules/sim-imports.mdc index 19da378bccf..95d111c7747 100644 --- a/.cursor/rules/sim-imports.mdc +++ b/.cursor/rules/sim-imports.mdc @@ -13,8 +13,8 @@ globs: ["apps/sim/**/*.ts","apps/sim/**/*.tsx"] ```typescript // ✓ Good +import { Chip } from '@sim/emcn' import { useWorkflowStore } from '@/stores/workflows/store' -import { Button } from '@/components/ui/button' // ✗ Bad import { useWorkflowStore } from '../../../stores/workflows/store' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.test.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.test.tsx index fe74a0467e4..76a6ab1dc66 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.test.tsx @@ -20,6 +20,13 @@ const { SECRET, searchTargetRef } = vi.hoisted(() => ({ })) vi.mock('@sim/emcn', () => ({ + Chip: ({ + onClick, + disabled, + 'aria-label': label, + }: React.ButtonHTMLAttributes) => ( + , -})) - vi.mock('next/navigation', () => ({ useParams: () => ({ workspaceId: 'workspace-1' }), })) @@ -254,3 +257,25 @@ describe('Code password masking', () => { expect(highlighted()).toContain(SECRET_MATCH) }) }) + +describe('Code copy action', () => { + it('copies the current value through the shared chip action', () => { + const writeText = vi.fn().mockResolvedValue(undefined) + vi.stubGlobal('navigator', { clipboard: { writeText } }) + vi.useFakeTimers() + act(() => + root.render( + + ) + ) + act(() => container.querySelector('button[aria-label="Copy code"]')!.click()) + expect(writeText).toHaveBeenCalledExactlyOnceWith(SECRET) + act(() => vi.advanceTimersByTime(2000)) + vi.useRealTimers() + }) +}) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.tsx index e4fe0f53f44..191fa72fb71 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.tsx @@ -1,6 +1,7 @@ import type { ReactElement } from 'react' import { memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' import { + Chip, CODE_LINE_HEIGHT_PX, Code as CodeEditor, calculateGutterWidth, @@ -10,11 +11,10 @@ import { highlight, languages, } from '@sim/emcn' -import { Check, Wand } from '@sim/emcn/icons' +import { Check } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { useParams } from 'next/navigation' import Editor from 'react-simple-code-editor' -import { Button } from '@/components/ui/button' import { CodeLanguage } from '@/lib/execution/languages' import { isLikelyReferenceSegment, @@ -43,6 +43,7 @@ import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/c import type { WandControlHandlers } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block' import { useActiveSearchTarget } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/providers/active-search-target-provider' import { restoreCursorAfterInsertion } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/utils' +import { WandButton } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-button' import { WandPromptBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar' import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes' import { useWand } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand' @@ -906,22 +907,12 @@ export const Code = memo(function Code({ return ( <> {showCopyButton && code && ( - + /> )} {!hideInternalWand && ( - - + /> )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.test.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.test.tsx index 9585aa4c5d0..8f3b40f5cd8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/long-input/long-input.test.tsx @@ -10,6 +10,13 @@ const { SECRET, searchTargetRef } = vi.hoisted(() => ({ })) vi.mock('@sim/emcn', () => ({ + Chip: ({ + onClick, + disabled, + 'aria-label': label, + }: React.ButtonHTMLAttributes) => ( +