From ea93a7b5595f5c9d278f88713ff301e38359c93b Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Sat, 1 Aug 2026 11:06:07 -0700 Subject: [PATCH 01/10] border styling --- .claude/rules/sim-styling.md | 10 ++++- .../message/components/markdown-renderer.tsx | 12 +++--- .../hero-chat-loop/hero-chat-loop.tsx | 4 +- .../landing-preview-logs.tsx | 2 +- .../landing-preview-resource.tsx | 2 +- .../files/components/files-hero-loop.tsx | 2 +- .../components/knowledge-hero-loop.tsx | 2 +- .../logs/components/logs-hero-loop.tsx | 2 +- apps/sim/app/_styles/globals.css | 38 ++++++++++++++----- apps/sim/app/layout.tsx | 2 +- .../components/resource/resource.tsx | 2 +- .../rich-markdown-editor.css | 10 ++--- .../components/chat-content/chat-content.tsx | 14 +++---- .../components/options/options.tsx | 2 +- .../components/question/question.tsx | 2 +- .../components/special-tags/special-tags.tsx | 2 +- .../suggested-actions/suggested-actions.tsx | 2 +- .../components/base-card/base-card.tsx | 4 +- .../workflow-mcp-servers.tsx | 2 +- .../workflow-controls/workflow-controls.tsx | 4 +- apps/sim/tailwind.config.ts | 18 ++++++++- .../emcn/src/components/chip-tag/chip-tag.tsx | 6 +-- packages/emcn/src/components/modal/modal.tsx | 3 +- .../src/note/note-block-view.tsx | 4 +- 24 files changed, 98 insertions(+), 53 deletions(-) diff --git a/.claude/rules/sim-styling.md b/.claude/rules/sim-styling.md index 3f926b78828..ec6165e9c6c 100644 --- a/.claude/rules/sim-styling.md +++ b/.claude/rules/sim-styling.md @@ -48,7 +48,15 @@ Icons default `size-[14px]`. Equal h/w → `size-*` (`size-[14px]`, `size-4`), n ## Color Tokens -Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text-icon`; borders `--border-1` (fields) / `--border` (dividers); surfaces `--surface-5` (light) / `--surface-4` (dark); active row `--surface-active`; error `--text-error`. No focus rings on chip surfaces. +Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text-icon`; neutral borders and dividers `--border` (`--border-1` and `--border-muted` are legacy aliases resolving to it; `--divider` is retired); surfaces `--surface-5` (light) / `--surface-4` (dark); active row `--surface-active`; error `--text-error`. No focus rings on chip surfaces. + +### Line weight + +Neutral border geometry comes from `--border-width`: `1px` by default, dropping to `0.5px` under `@media (min-resolution: 2dppx)` so hidpi displays get a true hairline. Tailwind's `border*` and `divide-*` utilities resolve through it, as do `h-px`/`w-px` — the `px` key is overridden on **`spacing`**, not on `width`/`height`, so a hairline and the `-right-px`/`inset-px` offsets that position it stay in agreement. + +**Tune line weight on `--border`, never on `--border-width`.** Browsers floor a border to whole device pixels, so on a 2dppx display every value in `(0, 1px)` collapses to the same single-pixel hairline and the next drawable step is a full `1px` — double. Width has exactly one usable position; perceived weight is a color property. Light mode gains weight by darkening `--border`, dark mode by lightening it. + +Draw a line with a real `border-*` utility. Never hand-roll one as `shadow-[inset_0_-1px_0_…]` — box-shadow has its own width and cannot follow the token, so such a line silently renders at double weight against every neighbor. Use an explicit numeric width (or a `ring`/`outline`) only when the line is intentionally emphasized, e.g. focus and selection affordances. ## Chip Components (consumer usage) diff --git a/apps/sim/app/(interfaces)/chat/components/message/components/markdown-renderer.tsx b/apps/sim/app/(interfaces)/chat/components/message/components/markdown-renderer.tsx index 31d02bdf51c..2337df8e7d8 100644 --- a/apps/sim/app/(interfaces)/chat/components/message/components/markdown-renderer.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message/components/markdown-renderer.tsx @@ -91,8 +91,8 @@ const COMPONENTS = { } return ( -
-
+
+
{codeProps.className?.replace('language-', '') || 'code'} @@ -115,12 +115,12 @@ const COMPONENTS = { ), blockquote: ({ children }: React.HTMLAttributes) => ( -
+
{children}
), - hr: () =>
, + hr: () =>
, a: ({ href, children, ...props }: React.AnchorHTMLAttributes) => ( @@ -139,10 +139,10 @@ const COMPONENTS = { {children} ), tbody: ({ children }: React.HTMLAttributes) => ( - {children} + {children} ), tr: ({ children }: React.HTMLAttributes) => ( - + {children} ), diff --git a/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx b/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx index 8c37423abef..6f430ec2660 100644 --- a/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx +++ b/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx @@ -53,7 +53,7 @@ interface HeroChatLoopProps { * screenshot is invisible. The reply STREAMS in word by word (the way the * real Mothership streams its responses); once the text completes, the * "Suggested follow-ups" block (the real special-tags markup: numbered - * `--divider`-ruled rows with a trailing arrow) and the action row land + * `--border`-ruled rows with a trailing arrow) and the action row land * together; under `prefers-reduced-motion` it appears whole. * The content column is centered and capped (like the real full-width * MothershipChat) so it reads right both full-width (stage collapsed) and at @@ -153,7 +153,7 @@ export function HeroChatLoop({ phase, fading }: HeroChatLoopProps) { 0 && 'border-t' )} > diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx index 3cb17549ee2..06d0d287411 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx @@ -233,7 +233,7 @@ export function LandingPreviewLogs() { - + {COL_HEADERS.map(({ key, label }) => ( ))} - + {columns.map((col) => ( {COL_HEADERS.map((header) => ( diff --git a/apps/sim/app/(landing)/knowledge/components/knowledge-hero-loop.tsx b/apps/sim/app/(landing)/knowledge/components/knowledge-hero-loop.tsx index dc262b2d24b..c54861f5951 100644 --- a/apps/sim/app/(landing)/knowledge/components/knowledge-hero-loop.tsx +++ b/apps/sim/app/(landing)/knowledge/components/knowledge-hero-loop.tsx @@ -209,7 +209,7 @@ export function KnowledgeHeroLoop() { - + {COL_HEADERS.map((header) => ( ))} - + {COL_HEADERS.map((label) => ( )} - {/* + {/* Workspace layout dimensions: set CSS vars before hydration to avoid layout jump. IMPORTANT: These hardcoded values must stay in sync with stores/constants.ts diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx index 707cfdaeb9f..718617f30bc 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx @@ -339,7 +339,7 @@ const ResourceTable = memo(function ResourceTable({
{hasCheckbox && ( diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css index cfb5e9a2ec3..3fcfb93ba84 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css @@ -14,7 +14,7 @@ max-width: 100%; height: auto; border-radius: 8px; - border: 1px solid var(--border); + border: var(--border-width) solid var(--border); } /* One consistent ring for every node that can only be selected as a whole (divider, image, @@ -204,7 +204,7 @@ width: 16px; height: 16px; margin: 0; - border: 1px solid var(--border-1); + border: var(--border-width) solid var(--border-1); border-radius: 3px; background: transparent; cursor: pointer; @@ -224,7 +224,7 @@ } .rich-markdown-prose blockquote { - border-left: 2px solid var(--divider); + border-left: 2px solid var(--border); padding-left: 1rem; color: var(--text-primary); font-style: italic; @@ -331,7 +331,7 @@ .rich-markdown-prose hr { border: none; - border-top: 1px solid var(--divider); + border-top: var(--border-width) solid var(--border); margin: 1.5em 0; } @@ -366,7 +366,7 @@ .rich-markdown-prose th, .rich-markdown-prose td { position: relative; - border: 1px solid var(--divider); + border: var(--border-width) solid var(--border); padding: 0.5rem 0.75rem; text-align: left; vertical-align: top; diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx index abedf727bf4..1966672184c 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx @@ -49,7 +49,7 @@ const PROSE_CLASSES = cn( 'prose-ul:my-4 prose-ol:my-4', 'prose-strong:font-[600] prose-strong:text-[var(--text-primary)]', 'prose-a:text-[var(--text-primary)] prose-a:underline prose-a:decoration-dashed prose-a:underline-offset-4', - 'prose-hr:border-[var(--divider)] prose-hr:my-6', + 'prose-hr:border-[var(--border)] prose-hr:my-6', 'prose-table:my-0' ) @@ -209,7 +209,7 @@ const MARKDOWN_COMPONENTS = { return ( {children} @@ -219,7 +219,7 @@ const MARKDOWN_COMPONENTS = { return ( {children} @@ -241,8 +241,8 @@ const MARKDOWN_COMPONENTS = { const html = highlight(codeString.trimEnd(), language) return ( -
-
+
+
{language || 'code'} +
{children}
) @@ -380,7 +380,7 @@ const MARKDOWN_COMPONENTS = { src={src} alt={alt ?? ''} loading='lazy' - className='my-4 h-auto max-w-full rounded-lg border border-[var(--divider)]' + className='my-4 h-auto max-w-full rounded-lg border border-[var(--border)]' /> ) }, diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx index ea0f2fc82a2..5b0206c90ae 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx @@ -15,7 +15,7 @@ export function Options({ items, onSelect }: OptionsProps) { key={item.id} type='button' onClick={() => onSelect?.(item.id)} - className='rounded-full border border-[var(--divider)] bg-[var(--bg)] px-3.5 py-1.5 font-[430] font-[family-name:var(--font-inter)] text-[var(--text-primary)] text-sm leading-5 transition-colors hover-hover:bg-[var(--surface-5)]' + className='rounded-full border border-[var(--border)] bg-[var(--bg)] px-3.5 py-1.5 font-[430] font-[family-name:var(--font-inter)] text-[var(--text-primary)] text-sm leading-5 transition-colors hover-hover:bg-[var(--surface-5)]' > {item.label} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/question/question.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/question/question.tsx index 53811b94c04..44b27dba5ff 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/question/question.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/question/question.tsx @@ -47,7 +47,7 @@ export function parseQuestionAnswerMessage( } const OPTION_ROW_CLASSES = - 'flex items-center gap-2 border-[var(--divider)] px-2 py-2 text-left transition-colors' + 'flex items-center gap-2 border-[var(--border)] px-2 py-2 text-left transition-colors' /** Ghost icon-button chrome shared by the stepper chevrons and the dismiss X. */ const ICON_BUTTON_CLASSES = 'relative size-[14px] flex-shrink-0 p-0' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx index 2dd4e87562d..38ebfc3a8f6 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx @@ -1268,7 +1268,7 @@ function OptionsDisplay({ data, onSelect }: OptionsDisplayProps) { disabled={disabled} onClick={() => onSelect?.(title)} className={cn( - 'flex items-center gap-2 border-[var(--divider)] px-2 py-2 text-left transition-colors', + 'flex items-center gap-2 border-[var(--border)] px-2 py-2 text-left transition-colors', disabled ? 'cursor-not-allowed' : 'hover-hover:bg-[var(--surface-5)]', i > 0 && 'border-t' )} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx index 736bf30fefa..a0d97220f45 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx @@ -396,7 +396,7 @@ export function SuggestedActions({ onSelectPrompt }: SuggestedActionsProps) { type='button' onClick={() => handleSelect(action, i)} className={cn( - 'flex items-center gap-2 border-[var(--divider)] px-2 py-2 text-left transition-colors hover-hover:bg-[var(--surface-5)]', + 'flex items-center gap-2 border-[var(--border)] px-2 py-2 text-left transition-colors hover-hover:bg-[var(--surface-5)]', i > 0 && 'border-t' )} > diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/base-card/base-card.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/base-card/base-card.tsx index b16a2d3ef27..ceb77fd923c 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/base-card/base-card.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/base-card/base-card.tsx @@ -48,7 +48,7 @@ export function BaseCardSkeleton() {
-
+
@@ -219,7 +219,7 @@ export function BaseCard({ )}
-
+

diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx index 69fed161b7e..f916e798322 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx @@ -598,7 +598,7 @@ function ServerDetailView({ canManage, workspaceId, serverId, onBack }: ServerDe {activeConfigTab === 'cursor' && ( -

+
@@ -176,7 +176,7 @@ export const WorkflowControls = memo(function WorkflowControls() { -
+
diff --git a/apps/sim/tailwind.config.ts b/apps/sim/tailwind.config.ts index 5fd3276e71d..796ec178263 100644 --- a/apps/sim/tailwind.config.ts +++ b/apps/sim/tailwind.config.ts @@ -62,6 +62,16 @@ export default { }, spacing: { '4.5': '18px', + /** + * Hairline scale key. Overriding `spacing` rather than `width`/`height` + * keeps every derived scale in agreement — a `w-px` line and the + * `-right-px` offset that positions it resolve to the same value, which + * a width-only override desynchronizes by half a device pixel. + */ + px: 'var(--border-width)', + }, + borderWidth: { + DEFAULT: 'var(--border-width)', }, colors: { background: 'hsl(var(--background))', @@ -94,7 +104,13 @@ export default { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))', }, - border: 'hsl(var(--border))', + /** + * Neutral border colors are plain hex, not the HSL triplets the shadcn + * keys below use — `hsl(var(--border))` would be invalid CSS and get + * dropped, leaving the utility to fall through to the `*` border-color + * rule in globals by accident. + */ + border: 'var(--border)', input: 'hsl(var(--input))', ring: 'hsl(var(--ring))', chart: { diff --git a/packages/emcn/src/components/chip-tag/chip-tag.tsx b/packages/emcn/src/components/chip-tag/chip-tag.tsx index e69ae59c429..c19fbba8277 100644 --- a/packages/emcn/src/components/chip-tag/chip-tag.tsx +++ b/packages/emcn/src/components/chip-tag/chip-tag.tsx @@ -37,10 +37,10 @@ const chipTagVariants = cva( mono: 'h-5 gap-[3px] px-1 bg-[var(--surface-5)] text-[var(--text-primary)] dark:bg-[var(--surface-4)]', field: 'h-5 gap-[3px] px-1 bg-[var(--surface-6)] text-[var(--text-primary)] dark:bg-[var(--surface-3)]', - gray: 'h-5 gap-[3px] px-1 bg-[var(--surface-5)] text-[var(--text-secondary)] shadow-[inset_0_0_0_1px_var(--border-1)]', + gray: 'h-5 gap-[3px] px-1 border border-[var(--border-1)] bg-[var(--surface-5)] text-[var(--text-secondary)]', solid: 'h-5 gap-[3px] px-1 bg-[var(--text-secondary)] text-[var(--text-inverse)]', invite: - 'h-5 gap-1.5 px-1 bg-[var(--surface-5)] text-[var(--text-body)] shadow-[inset_0_0_0_1px_var(--border-1)] dark:bg-[var(--surface-4)]', + 'h-5 gap-1.5 px-1 border border-[var(--border-1)] bg-[var(--surface-5)] text-[var(--text-body)] dark:bg-[var(--surface-4)]', }, invalid: { true: '', false: '' }, }, @@ -48,7 +48,7 @@ const chipTagVariants = cva( { variant: 'invite', invalid: true, - className: 'bg-[var(--badge-error-bg)] text-[var(--text-error)] shadow-none', + className: 'bg-[var(--badge-error-bg)] text-[var(--text-error)] border-transparent', }, ], defaultVariants: { variant: 'mono', invalid: false }, diff --git a/packages/emcn/src/components/modal/modal.tsx b/packages/emcn/src/components/modal/modal.tsx index 4229c3779bb..05b2a0da472 100644 --- a/packages/emcn/src/components/modal/modal.tsx +++ b/packages/emcn/src/components/modal/modal.tsx @@ -245,7 +245,8 @@ const ModalContent = React.forwardRef< ref={ref} className={cn( 'pointer-events-auto flex max-h-[84vh] flex-col text-small', - !bare && 'overflow-hidden rounded-xl bg-[var(--bg)] ring-1 ring-foreground/10', + !bare && + 'overflow-hidden rounded-xl bg-[var(--bg)] ring-[length:var(--border-width)] ring-foreground/10', ANIMATION_CLASSES, 'data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 duration-200', MODAL_SIZES[size], diff --git a/packages/workflow-renderer/src/note/note-block-view.tsx b/packages/workflow-renderer/src/note/note-block-view.tsx index 4b0a3e50159..f1650ddf6ed 100644 --- a/packages/workflow-renderer/src/note/note-block-view.tsx +++ b/packages/workflow-renderer/src/note/note-block-view.tsx @@ -137,7 +137,7 @@ const NOTE_COMPONENTS = { {children} ), blockquote: ({ children }: { children?: ReactNode }) => ( -
+
{children}
), @@ -215,7 +215,7 @@ export function NoteBlockView({ > {actionBar} -
+
Date: Mon, 3 Aug 2026 20:36:48 -0700 Subject: [PATCH 02/10] improvement(platform): migrate off lucide-react, flatten the font-weight scale, and retire scheduled tasks and workflow references --- .claude/rules/emcn-components.md | 4 +- .claude/rules/sim-styling.md | 10 +- apps/docs/components/ai/ask-ai-panel.tsx | 4 +- apps/docs/components/ai/ask-ai.tsx | 6 +- .../components/docs-layout/page-footer.tsx | 2 +- .../docs-layout/page-navigation-arrows.tsx | 2 +- apps/docs/components/page-actions.tsx | 4 +- apps/docs/components/ui/code-block.tsx | 6 +- apps/docs/components/ui/faq.tsx | 2 +- apps/docs/components/ui/heading.tsx | 2 +- apps/docs/components/ui/response-section.tsx | 2 +- apps/docs/components/ui/search-trigger.tsx | 2 +- .../workflow-preview/block-icons.tsx | 8 +- .../workflow-preview/block-inspector.tsx | 2 +- .../workflow-preview/output-bundle.tsx | 2 +- .../workflow-preview/workflow-preview.tsx | 4 +- apps/docs/content/docs/de/copilot/index.mdx | 6 +- apps/docs/content/docs/es/copilot/index.mdx | 6 +- apps/docs/content/docs/fr/copilot/index.mdx | 6 +- apps/docs/content/docs/ja/copilot/index.mdx | 6 +- apps/docs/content/docs/zh/copilot/index.mdx | 6 +- apps/docs/package.json | 1 - .../(auth)/components/auth-submit-button.tsx | 1 - .../app/(auth)/components/password-input.tsx | 2 +- .../components/social-login-buttons.tsx | 3 - .../(auth)/components/sso-login-button.tsx | 1 - .../auth/password/password-auth.tsx | 8 +- .../chat/components/input/input.tsx | 8 +- .../chat/components/input/voice-input.tsx | 10 +- .../message-container/message-container.tsx | 2 +- .../message/components/file-download.tsx | 2 +- .../chat/components/message/message.tsx | 4 +- .../voice-interface/voice-interface.tsx | 2 +- .../[executionId]/resume-page-client.tsx | 2 +- .../changelog-actions/changelog-actions.tsx | 2 +- .../changelog-timeline/changelog-timeline.tsx | 2 +- .../components/auth-modal/auth-modal.tsx | 2 +- .../build-chat-animation.tsx | 3 +- .../components/feature-card/feature-card.tsx | 2 +- .../hero-chat-loop/hero-chat-loop.tsx | 6 +- .../components/hero-visual/stage-home.tsx | 4 +- .../hero/components/hero-visual/stage-kb.tsx | 2 +- .../landing-preview-chat/chat-input.tsx | 2 +- .../landing-preview-home.tsx | 5 +- .../landing-preview-sidebar.tsx | 20 +- .../landing-preview-stage-header.tsx | 5 +- .../preview-block-node.tsx | 2 +- .../components/mobile-nav/mobile-nav.tsx | 4 +- .../nav-menu-chip/nav-menu-chip.tsx | 7 +- .../(landing)/components/navbar/navbar.tsx | 10 +- .../components/share-button/share-button.tsx | 5 +- .../solutions-pill-cta/solutions-pill-cta.tsx | 2 +- .../components/contact-form/contact-form.tsx | 2 - .../demo/components/demo-form/demo-form.tsx | 3 - .../enterprise-home-stage.tsx | 4 +- .../enterprise-sidebar.tsx | 4 +- .../components/pricing-card/pricing-card.tsx | 8 +- .../scheduled-tasks-calendar-loop.tsx | 7 +- apps/sim/app/_styles/globals.css | 36 +- .../workflows/[id]/references/route.test.ts | 88 ----- .../api/workflows/[id]/references/route.ts | 37 -- apps/sim/app/f/[token]/public-file-auth.tsx | 4 +- .../sim/app/invite/components/status-card.tsx | 9 +- apps/sim/app/layout.tsx | 8 +- apps/sim/app/playground/page.tsx | 4 +- apps/sim/app/unsubscribe/unsubscribe.tsx | 27 +- .../components/credential-detail-layout.tsx | 2 +- .../generate-prompt-control.tsx | 5 +- .../[workspaceId]/components/error/error.tsx | 6 +- .../[workspaceId]/components/index.ts | 1 + .../integration-tabs-header/index.ts | 1 + .../integration-tabs-header.tsx | 17 +- .../components/invite-modal/invite-modal.tsx | 1 - .../resource-header/resource-header.tsx | 16 +- .../resource-options/resource-options.tsx | 4 +- .../components/resource/resource.tsx | 3 +- .../file-viewer/editor-context-menu.tsx | 3 +- .../components/file-viewer/file-viewer.tsx | 4 +- .../file-viewer/preview-toolbar.tsx | 6 +- .../rich-markdown-editor/code-block.tsx | 8 +- .../mention/mention-list.test.tsx | 2 +- .../rich-markdown-editor/mention/types.ts | 2 +- .../menus/bubble-menu.tsx | 12 +- .../menus/link-hover-card.tsx | 4 +- .../rich-markdown-editor/menus/table-menu.tsx | 16 +- .../menus/toolbar-button.tsx | 4 +- .../rich-markdown-editor.css | 2 +- .../slash-command/commands.ts | 14 +- .../components/file-viewer/xlsx-preview.tsx | 2 +- .../agent-group/tool-permission-card.tsx | 10 +- .../components/chat-content/chat-content.tsx | 16 +- .../components/options/options.tsx | 2 +- .../components/special-tags/special-tags.tsx | 8 +- .../mothership-chat-skeleton.tsx | 2 +- .../mothership-chat/mothership-chat.tsx | 4 +- .../resource-content/resource-content.tsx | 33 +- .../queued-messages/queued-messages.tsx | 4 +- .../suggested-actions/suggested-actions.tsx | 91 ++--- .../attached-files-list.tsx | 2 +- .../user-input/components/constants.ts | 6 +- .../home/components/user-input/user-input.tsx | 6 +- .../user-message-content.tsx | 4 +- .../app/workspace/[workspaceId]/home/home.tsx | 4 +- .../integration-block-detail-fallback.tsx | 4 +- .../[block]/integration-block-detail.tsx | 2 +- .../[block]/integration-skills-section.tsx | 6 +- .../connect-slack-bot-modal.tsx | 4 +- .../integration-tabs-header/index.ts | 1 - .../showcase-with-explore.tsx | 4 +- .../integrations/integrations.tsx | 4 +- .../[workspaceId]/integrations/page.tsx | 2 +- .../knowledge/[id]/[documentId]/document.tsx | 5 +- .../knowledge/[id]/[documentId]/loading.tsx | 3 +- .../[workspaceId]/knowledge/[id]/base.tsx | 15 +- .../[id]/components/action-bar/action-bar.tsx | 8 +- .../add-connector-modal.tsx | 2 +- .../add-documents-modal.tsx | 4 +- .../connector-config-fields.tsx | 4 +- .../connectors-section/connectors-section.tsx | 24 +- .../edit-connector-modal.tsx | 6 +- .../create-base-modal/create-base-modal.tsx | 2 +- .../[workspaceId]/knowledge/knowledge.tsx | 3 - .../execution-snapshot/execution-snapshot.tsx | 6 +- .../components/trace-view/trace-view.tsx | 4 +- .../components/log-details/log-details.tsx | 17 +- .../app/workspace/[workspaceId]/not-found.tsx | 5 +- .../app/workspace/[workspaceId]/prefetch.ts | 31 +- .../calendar-toolbar/calendar-toolbar.tsx | 6 +- .../schedule-list-context-menu/index.ts | 1 - .../schedule-list-context-menu.tsx | 53 --- .../components/task-context-menu/index.ts | 1 - .../task-context-menu/task-context-menu.tsx | 110 ------ .../components/task-delete-dialog/index.ts | 1 - .../task-delete-dialog/task-delete-dialog.tsx | 98 ----- .../components/task-details-modal/index.ts | 1 - .../task-details-modal/task-details-modal.tsx | 106 ------ .../components/task-modal/index.ts | 1 - .../task-modal/recurrence-section.tsx | 301 --------------- .../components/task-modal/task-modal.tsx | 347 ------------------ .../[workspaceId]/scheduled-tasks/error.tsx | 15 - .../scheduled-tasks/hooks/use-calendar.ts | 180 --------- .../hooks/use-scheduled-tasks.ts | 286 --------------- .../[workspaceId]/scheduled-tasks/loading.tsx | 18 - .../[workspaceId]/scheduled-tasks/page.tsx | 22 -- .../scheduled-tasks/scheduled-tasks.tsx | 231 ------------ .../scheduled-tasks/search-params.ts | 78 ---- .../components/activity-log/activity-log.tsx | 2 +- .../settings/components/api-keys/api-keys.tsx | 4 +- .../settings/components/billing/billing.tsx | 6 +- .../password-detail/password-detail.tsx | 4 +- .../components/byok/byok-key-manager.tsx | 10 +- .../settings/components/copilot/copilot.tsx | 2 +- .../components/custom-tools/custom-tools.tsx | 3 +- .../settings/components/general/general.tsx | 6 +- .../inbox-settings-tab/inbox-settings-tab.tsx | 5 +- .../inbox-task-list/inbox-task-list.tsx | 2 +- .../mcp-server-form-modal.tsx | 2 +- .../settings/components/mcp/mcp.tsx | 3 +- .../row-actions-menu/row-actions-menu.tsx | 6 +- .../team-seats-overview.tsx | 2 +- .../components/teammates/teammates.tsx | 6 +- .../workflow-mcp-servers.tsx | 3 +- .../workspace/[workspaceId]/skills/page.tsx | 2 +- .../workspace/[workspaceId]/skills/skills.tsx | 5 +- .../new-column-dropdown.tsx | 2 +- .../components/row-modal/row-modal.tsx | 2 - .../components/table-grid/table-find.tsx | 3 +- .../workflow-sidebar/workflow-sidebar.tsx | 9 +- .../comparison-table/comparison-table.tsx | 2 +- .../components/plan-card/plan-card.tsx | 1 - .../components/action-bar/action-bar.tsx | 8 +- .../components/chat/chat.test.tsx | 7 +- .../w/[workflowId]/components/chat/chat.tsx | 12 +- .../components/chat-message/chat-message.tsx | 2 +- .../output-select/output-select.tsx | 6 +- .../components/command-list/command-list.tsx | 4 +- .../w/[workflowId]/components/error/index.tsx | 2 +- .../deploy-modal/components/api/api.tsx | 2 +- .../deploy-modal/components/chat/chat.tsx | 4 +- .../general/components/versions.tsx | 4 +- .../components/field-item/field-item.tsx | 2 +- .../connection-blocks/connection-blocks.tsx | 6 +- .../checkbox-list/checkbox-list.tsx | 4 +- .../sub-block/components/code/code.tsx | 4 +- .../condition-input/condition-input.tsx | 3 +- .../credential-selector.tsx | 12 +- .../document-tag-entry/document-tag-entry.tsx | 2 +- .../env-var-dropdown/env-var-dropdown.tsx | 2 +- .../components/eval-input/eval-input.tsx | 3 +- .../components/file-upload/file-upload.tsx | 2 +- .../components/filter-rule-row.tsx | 2 +- .../filter-builder/filter-builder.tsx | 2 +- .../grouped-checkbox-list.tsx | 4 +- .../knowledge-base-selector.tsx | 2 +- .../knowledge-tag-filters.tsx | 2 +- .../components/long-input/long-input.tsx | 4 +- .../messages-input/messages-input.tsx | 3 +- .../selector-combobox/selector-combobox.tsx | 2 +- .../components/short-input/short-input.tsx | 4 +- .../components/skill-input/skill-input.tsx | 4 +- .../slack-setup-wizard/slack-setup-wizard.tsx | 4 +- .../sort-builder/components/sort-rule-row.tsx | 2 +- .../components/sort-builder/sort-builder.tsx | 2 +- .../components/starter/input-format.tsx | 3 +- .../components/tag-dropdown/tag-dropdown.tsx | 6 +- .../components/tools/credential-selector.tsx | 4 +- .../tool-input/components/tools/parameter.tsx | 2 +- .../components/tool-input/tool-input.tsx | 16 +- .../variables-input/variables-input.tsx | 3 +- .../editor/components/sub-block/sub-block.tsx | 12 +- .../subflow-editor/subflow-editor.tsx | 2 +- .../panel/components/editor/editor.tsx | 8 +- .../panel/components/toolbar/toolbar.tsx | 2 +- .../w/[workflowId]/components/panel/panel.tsx | 5 +- .../workflow-search-replace.tsx | 2 +- .../components/subflows/loop/loop-config.ts | 4 +- .../subflows/parallel/parallel-config.ts | 4 +- .../filter-popover/filter-popover.tsx | 6 +- .../components/output-panel/output-panel.tsx | 10 +- .../toggle-button/toggle-button.tsx | 2 +- .../components/terminal/terminal.tsx | 14 +- .../[workflowId]/components/terminal/utils.ts | 21 +- .../components/variables/variables.tsx | 3 +- .../wand-prompt-bar/wand-prompt-bar.tsx | 6 +- .../workflow-controls/workflow-controls.tsx | 4 +- .../preview-editor/preview-editor.tsx | 27 +- .../components/subflow/subflow.tsx | 4 +- .../w/components/preview/preview.tsx | 2 +- .../collapsed-sidebar-menu.tsx | 9 +- .../components/file-list/file-list.tsx | 12 +- .../components/help-modal/help-modal.tsx | 6 +- .../w/components/sidebar/components/index.ts | 2 + .../command-items/command-items.tsx | 4 +- .../components/search-modal/search-modal.tsx | 19 +- .../settings-sidebar/settings-sidebar.tsx | 27 +- .../components/sidebar-footer/index.ts | 1 + .../sidebar-footer/sidebar-footer.tsx | 259 +++++++++++++ .../components/sidebar-section/index.ts | 1 + .../sidebar-section/sidebar-section.tsx | 119 ++++++ .../components/context-menu/context-menu.tsx | 22 +- .../components/folder-item/folder-item.tsx | 3 +- .../reference-tree/reference-tree.tsx | 68 ---- .../references-modal/references-modal.tsx | 74 ---- .../workflow-item/workflow-item.tsx | 20 +- .../view-invitations-menu-item.tsx | 2 +- .../workspace-header/workspace-header.tsx | 103 ++---- .../w/components/sidebar/constants.ts | 30 +- .../w/components/sidebar/sidebar.tsx | 258 ++++++------- apps/sim/blocks/blocks/chat_trigger.ts | 8 +- apps/sim/blocks/blocks/clickhouse.ts | 13 +- apps/sim/blocks/blocks/generic_webhook.ts | 8 +- apps/sim/blocks/blocks/input_trigger.ts | 8 +- apps/sim/blocks/blocks/manual_trigger.ts | 8 +- apps/sim/blocks/blocks/schedule.ts | 8 +- apps/sim/blocks/blocks/sftp.ts | 12 +- apps/sim/blocks/blocks/wait.ts | 8 +- apps/sim/blocks/custom/custom-block-icon.tsx | 5 +- apps/sim/components.json | 3 +- .../agent-stream/agent-stream-chrome.tsx | 12 +- .../sim/components/permissions/member-row.tsx | 2 +- .../components/pii/custom-patterns-editor.tsx | 4 +- apps/sim/components/settings/navigation.ts | 10 +- .../ui/generated-password-input.tsx | 2 +- apps/sim/components/ui/select.tsx | 2 +- .../content/blog/enterprise/components.tsx | 4 +- .../components/access-control.tsx | 2 +- .../components/group-detail.tsx | 24 +- .../components/custom-block-detail.tsx | 2 +- .../components/custom-blocks.tsx | 2 +- .../components/data-retention-settings.tsx | 3 +- apps/sim/ee/sso/components/sso-settings.tsx | 2 +- .../components/whitelabeling-settings.tsx | 2 +- .../components/fork-sync/fork-sync-view.tsx | 2 +- .../fork-workspace-modal.tsx | 4 +- .../ee/workspace-forking/components/forks.tsx | 7 +- .../company-domain/company-domain.ts | 2 +- .../enrichments/company-info/company-info.ts | 4 +- .../enrichments/phone-number/phone-number.ts | 2 +- apps/sim/hooks/queries/schedules.ts | 5 +- apps/sim/hooks/queries/workflow-references.ts | 39 -- apps/sim/hooks/use-workspace-invite-policy.ts | 48 +++ .../lib/api/contracts/workflow-references.ts | 54 --- apps/sim/lib/billing/workspace-permissions.ts | 17 + .../lib/copilot/tools/client/store-utils.ts | 2 +- apps/sim/lib/mothership/inbox/response.ts | 4 +- apps/sim/lib/posthog/events.ts | 5 - .../workflows/references/operations.test.ts | 309 ---------------- .../lib/workflows/references/operations.ts | 327 ----------------- apps/sim/package.json | 1 - apps/sim/stores/constants.ts | 4 +- apps/sim/stores/modals/search/store.ts | 6 +- apps/sim/tailwind.config.ts | 15 +- bun.lock | 6 - packages/emcn/package.json | 2 - .../emcn/src/components/button/button.tsx | 29 ++ .../components/calendar/calendar-day-cell.tsx | 1 - .../emcn/src/components/calendar/calendar.tsx | 18 +- .../emcn/src/components/checkbox/checkbox.tsx | 2 +- .../chip-copy-input/chip-copy-input.tsx | 2 +- .../chip-date-picker/chip-date-picker.tsx | 7 +- .../chip-dropdown/chip-dropdown.tsx | 4 +- .../src/components/chip-input/chip-input.tsx | 2 +- .../src/components/chip-modal/chip-modal.tsx | 19 +- .../components/chip-select/chip-select.tsx | 2 +- .../components/chip-switch/chip-switch.tsx | 1 - .../chip-time-picker/chip-time-picker.tsx | 8 +- packages/emcn/src/components/chip/chip.tsx | 19 +- packages/emcn/src/components/code/code.tsx | 2 +- .../emcn/src/components/combobox/combobox.tsx | 3 +- .../dropdown-menu/dropdown-menu.tsx | 48 ++- .../src/components/info-card/info-card.tsx | 4 +- .../src/components/input-otp/input-otp.tsx | 2 +- packages/emcn/src/components/modal/modal.tsx | 2 +- .../emcn/src/components/popover/popover.tsx | 4 +- .../progress-item/progress-item.tsx | 5 +- .../secret-reveal/secret-reveal.tsx | 6 +- .../src/components/tag-input/tag-input.tsx | 6 +- .../components/time-picker/time-picker.tsx | 2 +- packages/emcn/src/components/toast/toast.tsx | 3 +- .../{user-plus.tsx => arrow-left-right.tsx} | 12 +- packages/emcn/src/icons/arrow-up-left.tsx | 26 ++ packages/emcn/src/icons/ban.tsx | 26 ++ packages/emcn/src/icons/bold.tsx | 26 ++ packages/emcn/src/icons/box.tsx | 27 ++ packages/emcn/src/icons/brain-circuit.tsx | 28 ++ packages/emcn/src/icons/brain.tsx | 27 ++ packages/emcn/src/icons/bubble-chat-delay.tsx | 2 +- packages/emcn/src/icons/building.tsx | 27 ++ packages/emcn/src/icons/camera.tsx | 27 ++ packages/emcn/src/icons/chevron-up.tsx | 25 ++ packages/emcn/src/icons/chevrons-down-up.tsx | 26 ++ packages/emcn/src/icons/chevrons-up-down.tsx | 26 ++ packages/emcn/src/icons/circle-pause.tsx | 27 ++ packages/emcn/src/icons/circle-x.tsx | 27 ++ packages/emcn/src/icons/circle.tsx | 25 ++ packages/emcn/src/icons/clipboard.tsx | 2 +- packages/emcn/src/icons/code.tsx | 26 ++ packages/emcn/src/icons/compass.tsx | 27 ++ packages/emcn/src/icons/credit.tsx | 6 +- packages/emcn/src/icons/database.tsx | 8 +- packages/emcn/src/icons/eye-off.tsx | 2 +- packages/emcn/src/icons/file-text.tsx | 27 ++ packages/emcn/src/icons/files.tsx | 8 +- packages/emcn/src/icons/folder-input.tsx | 2 +- packages/emcn/src/icons/folder-open.tsx | 26 ++ packages/emcn/src/icons/form-input.tsx | 27 ++ packages/emcn/src/icons/globe.tsx | 27 ++ packages/emcn/src/icons/heading1.tsx | 28 ++ packages/emcn/src/icons/heading2.tsx | 28 ++ packages/emcn/src/icons/heading3.tsx | 28 ++ packages/emcn/src/icons/help-circle.tsx | 8 +- packages/emcn/src/icons/highlighter.tsx | 27 ++ packages/emcn/src/icons/index.ts | 55 ++- packages/emcn/src/icons/infinity.tsx | 25 ++ packages/emcn/src/icons/italic.tsx | 27 ++ packages/emcn/src/icons/library.tsx | 12 +- packages/emcn/src/icons/list-checks.tsx | 26 ++ packages/emcn/src/icons/list-ordered.tsx | 28 ++ packages/emcn/src/icons/list.tsx | 28 ++ packages/emcn/src/icons/menu.tsx | 27 ++ packages/emcn/src/icons/mic-off.tsx | 28 ++ packages/emcn/src/icons/minus.tsx | 25 ++ packages/emcn/src/icons/moon.tsx | 25 ++ packages/emcn/src/icons/more-vertical.tsx | 27 ++ packages/emcn/src/icons/music.tsx | 27 ++ packages/emcn/src/icons/panel-right.tsx | 26 ++ packages/emcn/src/icons/phone.tsx | 25 ++ packages/emcn/src/icons/pilcrow.tsx | 27 ++ packages/emcn/src/icons/pin-off.tsx | 2 +- packages/emcn/src/icons/pin.tsx | 2 +- packages/emcn/src/icons/repeat.tsx | 28 ++ packages/emcn/src/icons/rss.tsx | 27 ++ packages/emcn/src/icons/scissors.tsx | 28 ++ packages/emcn/src/icons/select-all.tsx | 2 +- packages/emcn/src/icons/send-to-back.tsx | 26 ++ packages/emcn/src/icons/send.tsx | 6 +- packages/emcn/src/icons/settings.tsx | 8 +- packages/emcn/src/icons/share.tsx | 28 ++ packages/emcn/src/icons/sparkles.tsx | 27 ++ packages/emcn/src/icons/square.tsx | 6 +- packages/emcn/src/icons/strikethrough.tsx | 26 ++ packages/emcn/src/icons/sun.tsx | 27 ++ packages/emcn/src/icons/table.tsx | 4 +- packages/emcn/src/icons/task.tsx | 9 +- packages/emcn/src/icons/text-quote.tsx | 26 ++ packages/emcn/src/icons/trash-outline.tsx | 29 -- packages/emcn/src/icons/trash.tsx | 19 +- packages/emcn/src/icons/trash2.tsx | 24 -- packages/emcn/src/icons/unlink.tsx | 27 ++ packages/emcn/src/icons/users.tsx | 10 +- packages/emcn/src/icons/wand.tsx | 27 ++ packages/emcn/src/icons/webhook.tsx | 28 ++ packages/emcn/src/icons/workflow.tsx | 7 +- packages/emcn/src/icons/zap.tsx | 25 ++ packages/emcn/src/index.ts | 11 + packages/workflow-renderer/package.json | 2 - .../src/edge/workflow-edge-view.tsx | 2 +- .../src/subflow/subflow-node-view.tsx | 4 +- 398 files changed, 2933 insertions(+), 4157 deletions(-) delete mode 100644 apps/sim/app/api/workflows/[id]/references/route.test.ts delete mode 100644 apps/sim/app/api/workflows/[id]/references/route.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/index.ts rename apps/sim/app/workspace/[workspaceId]/{integrations => }/components/integration-tabs-header/integration-tabs-header.tsx (52%) delete mode 100644 apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-list-context-menu/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/schedule-list-context-menu/schedule-list-context-menu.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-context-menu/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-context-menu/task-context-menu.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-delete-dialog/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-delete-dialog/task-delete-dialog.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-details-modal/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-details-modal/task-details-modal.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/error.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-scheduled-tasks.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/page.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/scheduled-tasks.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/search-params.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/index.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/index.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/sidebar-section.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/components/reference-tree/reference-tree.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/references-modal.tsx delete mode 100644 apps/sim/hooks/queries/workflow-references.ts create mode 100644 apps/sim/hooks/use-workspace-invite-policy.ts delete mode 100644 apps/sim/lib/api/contracts/workflow-references.ts delete mode 100644 apps/sim/lib/workflows/references/operations.test.ts delete mode 100644 apps/sim/lib/workflows/references/operations.ts rename packages/emcn/src/icons/{user-plus.tsx => arrow-left-right.tsx} (56%) create mode 100644 packages/emcn/src/icons/arrow-up-left.tsx create mode 100644 packages/emcn/src/icons/ban.tsx create mode 100644 packages/emcn/src/icons/bold.tsx create mode 100644 packages/emcn/src/icons/box.tsx create mode 100644 packages/emcn/src/icons/brain-circuit.tsx create mode 100644 packages/emcn/src/icons/brain.tsx create mode 100644 packages/emcn/src/icons/building.tsx create mode 100644 packages/emcn/src/icons/camera.tsx create mode 100644 packages/emcn/src/icons/chevron-up.tsx create mode 100644 packages/emcn/src/icons/chevrons-down-up.tsx create mode 100644 packages/emcn/src/icons/chevrons-up-down.tsx create mode 100644 packages/emcn/src/icons/circle-pause.tsx create mode 100644 packages/emcn/src/icons/circle-x.tsx create mode 100644 packages/emcn/src/icons/circle.tsx create mode 100644 packages/emcn/src/icons/code.tsx create mode 100644 packages/emcn/src/icons/compass.tsx create mode 100644 packages/emcn/src/icons/file-text.tsx create mode 100644 packages/emcn/src/icons/folder-open.tsx create mode 100644 packages/emcn/src/icons/form-input.tsx create mode 100644 packages/emcn/src/icons/globe.tsx create mode 100644 packages/emcn/src/icons/heading1.tsx create mode 100644 packages/emcn/src/icons/heading2.tsx create mode 100644 packages/emcn/src/icons/heading3.tsx create mode 100644 packages/emcn/src/icons/highlighter.tsx create mode 100644 packages/emcn/src/icons/infinity.tsx create mode 100644 packages/emcn/src/icons/italic.tsx create mode 100644 packages/emcn/src/icons/list-checks.tsx create mode 100644 packages/emcn/src/icons/list-ordered.tsx create mode 100644 packages/emcn/src/icons/list.tsx create mode 100644 packages/emcn/src/icons/menu.tsx create mode 100644 packages/emcn/src/icons/mic-off.tsx create mode 100644 packages/emcn/src/icons/minus.tsx create mode 100644 packages/emcn/src/icons/moon.tsx create mode 100644 packages/emcn/src/icons/more-vertical.tsx create mode 100644 packages/emcn/src/icons/music.tsx create mode 100644 packages/emcn/src/icons/panel-right.tsx create mode 100644 packages/emcn/src/icons/phone.tsx create mode 100644 packages/emcn/src/icons/pilcrow.tsx create mode 100644 packages/emcn/src/icons/repeat.tsx create mode 100644 packages/emcn/src/icons/rss.tsx create mode 100644 packages/emcn/src/icons/scissors.tsx create mode 100644 packages/emcn/src/icons/send-to-back.tsx create mode 100644 packages/emcn/src/icons/share.tsx create mode 100644 packages/emcn/src/icons/sparkles.tsx create mode 100644 packages/emcn/src/icons/strikethrough.tsx create mode 100644 packages/emcn/src/icons/sun.tsx create mode 100644 packages/emcn/src/icons/text-quote.tsx delete mode 100644 packages/emcn/src/icons/trash-outline.tsx delete mode 100644 packages/emcn/src/icons/trash2.tsx create mode 100644 packages/emcn/src/icons/unlink.tsx create mode 100644 packages/emcn/src/icons/wand.tsx create mode 100644 packages/emcn/src/icons/webhook.tsx create mode 100644 packages/emcn/src/icons/zap.tsx diff --git a/.claude/rules/emcn-components.md b/.claude/rules/emcn-components.md index 23491d6aaaf..8b9cc4e450f 100644 --- a/.claude/rules/emcn-components.md +++ b/.claude/rules/emcn-components.md @@ -1,6 +1,6 @@ --- paths: - - "apps/sim/components/emcn/**" + - "packages/emcn/**" --- # EMCN Components @@ -20,7 +20,7 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items ## Component catalogue -- **`Chip` / `ChipLink`** — the pill button (`
), }) @@ -46,7 +46,7 @@ export function AskAI({ locale }: AskAIProps) { onClick={handleOpen} className='fixed right-4 bottom-4 z-50 flex h-11 items-center gap-1.5 rounded-full border border-[var(--border-1)] bg-[var(--surface-5)] px-4 font-season text-[var(--text-body)] text-sm shadow-[var(--shadow-medium)] transition-colors hover:bg-[var(--surface-active)] dark:bg-[var(--surface-4)]' > - + Ask Sim )} diff --git a/apps/docs/components/docs-layout/page-footer.tsx b/apps/docs/components/docs-layout/page-footer.tsx index c0796c1f359..7031211fe84 100644 --- a/apps/docs/components/docs-layout/page-footer.tsx +++ b/apps/docs/components/docs-layout/page-footer.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from 'react' -import { ChevronLeft, ChevronRight } from 'lucide-react' +import { ChevronLeft, ChevronRight } from '@sim/emcn/icons' import Link from 'next/link' interface PageNeighbour { diff --git a/apps/docs/components/docs-layout/page-navigation-arrows.tsx b/apps/docs/components/docs-layout/page-navigation-arrows.tsx index 4701e2876a4..2aa973aa21e 100644 --- a/apps/docs/components/docs-layout/page-navigation-arrows.tsx +++ b/apps/docs/components/docs-layout/page-navigation-arrows.tsx @@ -1,6 +1,6 @@ 'use client' -import { ChevronLeft, ChevronRight } from 'lucide-react' +import { ChevronLeft, ChevronRight } from '@sim/emcn/icons' import Link from 'next/link' interface PageNavigationArrowsProps { diff --git a/apps/docs/components/page-actions.tsx b/apps/docs/components/page-actions.tsx index fe65e08e386..53c92c8d087 100644 --- a/apps/docs/components/page-actions.tsx +++ b/apps/docs/components/page-actions.tsx @@ -1,8 +1,8 @@ 'use client' import { Chip } from '@sim/emcn' +import { Check, Duplicate } from '@sim/emcn/icons' import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button' -import { Check, Copy } from 'lucide-react' export function LLMCopyButton({ content }: { content: string }) { const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(content)) @@ -10,7 +10,7 @@ export function LLMCopyButton({ content }: { content: string }) { return ( {checked ? 'Copied' : 'Copy page'} diff --git a/apps/docs/components/ui/code-block.tsx b/apps/docs/components/ui/code-block.tsx index 839a87ebff4..2d7b3c450f5 100644 --- a/apps/docs/components/ui/code-block.tsx +++ b/apps/docs/components/ui/code-block.tsx @@ -1,8 +1,8 @@ 'use client' import { useState } from 'react' +import { Check, Duplicate } from '@sim/emcn/icons' import { CodeBlock as FumadocsCodeBlock } from 'fumadocs-ui/components/codeblock' -import { Check, Copy } from 'lucide-react' import { cn } from '@/lib/utils' export function CodeBlock(props: React.ComponentProps) { @@ -31,9 +31,9 @@ export function CodeBlock(props: React.ComponentProps) > {copied ? ( - + ) : ( - + )} diff --git a/apps/docs/components/ui/faq.tsx b/apps/docs/components/ui/faq.tsx index da6cd6cd2b5..6b2d241b59b 100644 --- a/apps/docs/components/ui/faq.tsx +++ b/apps/docs/components/ui/faq.tsx @@ -1,7 +1,7 @@ 'use client' import { useState } from 'react' -import { ChevronRight } from 'lucide-react' +import { ChevronRight } from '@sim/emcn/icons' import { serializeJsonLd } from '@/lib/json-ld' import { cn } from '@/lib/utils' diff --git a/apps/docs/components/ui/heading.tsx b/apps/docs/components/ui/heading.tsx index b012e9f00ce..cd259daa121 100644 --- a/apps/docs/components/ui/heading.tsx +++ b/apps/docs/components/ui/heading.tsx @@ -1,7 +1,7 @@ 'use client' import { type ComponentPropsWithoutRef, useState } from 'react' -import { Check, Link } from 'lucide-react' +import { Check, Link } from '@sim/emcn/icons' import { cn } from '@/lib/utils' type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' diff --git a/apps/docs/components/ui/response-section.tsx b/apps/docs/components/ui/response-section.tsx index c19353a1065..44a7c0ebf08 100644 --- a/apps/docs/components/ui/response-section.tsx +++ b/apps/docs/components/ui/response-section.tsx @@ -1,7 +1,7 @@ 'use client' import { useEffect, useRef, useState } from 'react' -import { ChevronDown } from 'lucide-react' +import { ChevronDown } from '@sim/emcn/icons' import { cn } from '@/lib/utils' interface ResponseSectionProps { diff --git a/apps/docs/components/ui/search-trigger.tsx b/apps/docs/components/ui/search-trigger.tsx index 7e222fac26b..2577f6a2585 100644 --- a/apps/docs/components/ui/search-trigger.tsx +++ b/apps/docs/components/ui/search-trigger.tsx @@ -6,7 +6,7 @@ import { chipGeometryClass, TRIGGER_BORDER_CLASS, } from '@sim/emcn' -import { Search } from 'lucide-react' +import { Search } from '@sim/emcn/icons' import { cn } from '@/lib/utils' export function SearchTrigger() { diff --git a/apps/docs/components/workflow-preview/block-icons.tsx b/apps/docs/components/workflow-preview/block-icons.tsx index b759375a56b..aa2f297d619 100644 --- a/apps/docs/components/workflow-preview/block-icons.tsx +++ b/apps/docs/components/workflow-preview/block-icons.tsx @@ -1,5 +1,5 @@ import type { ComponentType, SVGProps } from 'react' -import { Clock, Database, Layers, Repeat, Table } from 'lucide-react' +import { Clock, Database, Repeat, Split, Table } from '@sim/emcn/icons' import { ApiIcon, ChartBarIcon, @@ -21,7 +21,7 @@ import { blockTypeToIconMap } from '@/components/ui/icon-mapping' /** * The two Sim-specific block glyphs we need, ported verbatim from * `apps/sim/components/icons.tsx` so the preview matches the real builder. - * Other block types fall back to lucide-react stand-ins for now. + * Other block types fall back to `@sim/emcn/icons` stand-ins for now. */ export function StartIcon(props: SVGProps) { return ( @@ -76,7 +76,7 @@ export function AgentIcon(props: SVGProps) { ) } -/** Block type → glyph. Brand glyphs from the app for core blocks; lucide stand-ins for the rest. */ +/** Block type → glyph. Brand glyphs from the app for core blocks; `@sim/emcn/icons` stand-ins for the rest. */ export const BLOCK_ICONS: Record> = { starter: StartIcon, start_trigger: StartIcon, @@ -97,7 +97,7 @@ export const BLOCK_ICONS: Record openWith(null)} className='absolute top-2 right-2 z-10 flex size-[28px] items-center justify-center rounded-[6px] border border-[var(--border-1)] bg-[var(--surface-4)] text-[var(--text-muted)] opacity-0 transition-opacity duration-150 hover:text-[var(--text-primary)] group-hover:opacity-100' > - +
diff --git a/apps/docs/content/docs/de/copilot/index.mdx b/apps/docs/content/docs/de/copilot/index.mdx index e9a92f7e209..4ccdbc8b4c7 100644 --- a/apps/docs/content/docs/de/copilot/index.mdx +++ b/apps/docs/content/docs/de/copilot/index.mdx @@ -5,7 +5,7 @@ title: Copilot import { Callout } from 'fumadocs-ui/components/callout' import { Card, Cards } from 'fumadocs-ui/components/card' import { Image } from '@/components/ui/image' -import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react' +import { Box, Brain, BrainCircuit, InfinityIcon, Task, Zap } from '@sim/emcn/icons' Copilot ist dein Assistent im Editor, der dir hilft, Workflows mit Sim Copilot zu erstellen und zu bearbeiten sowie diese zu verstehen und zu verbessern. Er kann: @@ -48,7 +48,7 @@ Diese kontextbezogenen Informationen helfen Copilot, genauere und relevantere Un - + Fragen } @@ -60,7 +60,7 @@ Diese kontextbezogenen Informationen helfen Copilot, genauere und relevantere Un - + Agent } diff --git a/apps/docs/content/docs/es/copilot/index.mdx b/apps/docs/content/docs/es/copilot/index.mdx index 4752570179b..49d5cc2b717 100644 --- a/apps/docs/content/docs/es/copilot/index.mdx +++ b/apps/docs/content/docs/es/copilot/index.mdx @@ -5,7 +5,7 @@ title: Copilot import { Callout } from 'fumadocs-ui/components/callout' import { Card, Cards } from 'fumadocs-ui/components/card' import { Image } from '@/components/ui/image' -import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react' +import { Box, Brain, BrainCircuit, InfinityIcon, Task, Zap } from '@sim/emcn/icons' Copilot es tu asistente integrado en el editor que te ayuda a crear y editar flujos de trabajo con Sim Copilot, así como a entenderlos y mejorarlos. Puede: @@ -48,7 +48,7 @@ Esta información contextual ayuda a Copilot a proporcionar asistencia más prec - + Preguntar } @@ -60,7 +60,7 @@ Esta información contextual ayuda a Copilot a proporcionar asistencia más prec - + Agente } diff --git a/apps/docs/content/docs/fr/copilot/index.mdx b/apps/docs/content/docs/fr/copilot/index.mdx index 53dc6c352c5..4a04abb26ed 100644 --- a/apps/docs/content/docs/fr/copilot/index.mdx +++ b/apps/docs/content/docs/fr/copilot/index.mdx @@ -5,7 +5,7 @@ title: Copilot import { Callout } from 'fumadocs-ui/components/callout' import { Card, Cards } from 'fumadocs-ui/components/card' import { Image } from '@/components/ui/image' -import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react' +import { Box, Brain, BrainCircuit, InfinityIcon, Task, Zap } from '@sim/emcn/icons' Copilot est votre assistant intégré à l'éditeur qui vous aide à créer et à modifier des flux de travail avec Sim Copilot, ainsi qu'à les comprendre et à les améliorer. Il peut : @@ -48,7 +48,7 @@ Ces informations contextuelles aident Copilot à fournir une assistance plus pr - + Demander } @@ -60,7 +60,7 @@ Ces informations contextuelles aident Copilot à fournir une assistance plus pr - + Agent } diff --git a/apps/docs/content/docs/ja/copilot/index.mdx b/apps/docs/content/docs/ja/copilot/index.mdx index d38f9a42cb2..e586d36c25f 100644 --- a/apps/docs/content/docs/ja/copilot/index.mdx +++ b/apps/docs/content/docs/ja/copilot/index.mdx @@ -5,7 +5,7 @@ title: Copilot import { Callout } from 'fumadocs-ui/components/callout' import { Card, Cards } from 'fumadocs-ui/components/card' import { Image } from '@/components/ui/image' -import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react' +import { Box, Brain, BrainCircuit, InfinityIcon, Task, Zap } from '@sim/emcn/icons' Copilotはエディター内のアシスタントで、Sim Copilotを使用してワークフローの構築や編集を支援し、それらを理解して改善するのに役立ちます。以下のことができます: @@ -48,7 +48,7 @@ Copilotはエディター内のアシスタントで、Sim Copilotを使用し - + 質問 } @@ -60,7 +60,7 @@ Copilotはエディター内のアシスタントで、Sim Copilotを使用し - + エージェント } diff --git a/apps/docs/content/docs/zh/copilot/index.mdx b/apps/docs/content/docs/zh/copilot/index.mdx index b37b1cdc6dc..38fdf56c9f7 100644 --- a/apps/docs/content/docs/zh/copilot/index.mdx +++ b/apps/docs/content/docs/zh/copilot/index.mdx @@ -5,7 +5,7 @@ title: Copilot import { Callout } from 'fumadocs-ui/components/callout' import { Card, Cards } from 'fumadocs-ui/components/card' import { Image } from '@/components/ui/image' -import { MessageCircle, Package, Zap, Infinity as InfinityIcon, Brain, BrainCircuit } from 'lucide-react' +import { Box, Brain, BrainCircuit, InfinityIcon, Task, Zap } from '@sim/emcn/icons' Copilot 是您编辑器中的助手,可帮助您使用 Sim Copilot 构建和编辑工作流,同时理解和改进它们。它可以: @@ -48,7 +48,7 @@ Copilot 是您编辑器中的助手,可帮助您使用 Sim Copilot 构建和 - + 提问 } @@ -60,7 +60,7 @@ Copilot 是您编辑器中的助手,可帮助您使用 Sim Copilot 构建和 - + 代理 } diff --git a/apps/docs/package.json b/apps/docs/package.json index 6ca41d1c268..a050907a1d8 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -28,7 +28,6 @@ "fumadocs-mdx": "14.3.2", "fumadocs-openapi": "10.8.1", "fumadocs-ui": "16.8.5", - "lucide-react": "^0.511.0", "next": "16.2.12", "next-themes": "^0.4.6", "react": "19.2.4", diff --git a/apps/sim/app/(auth)/components/auth-submit-button.tsx b/apps/sim/app/(auth)/components/auth-submit-button.tsx index ae9004b8188..5d37ecb12ea 100644 --- a/apps/sim/app/(auth)/components/auth-submit-button.tsx +++ b/apps/sim/app/(auth)/components/auth-submit-button.tsx @@ -32,7 +32,6 @@ export function AuthSubmitButton({ onClick={onClick} disabled={disabled || loading} fullWidth - flush className={AUTH_BUTTON_CLASS} > {loading ? ( diff --git a/apps/sim/app/(auth)/components/password-input.tsx b/apps/sim/app/(auth)/components/password-input.tsx index d602b24e25e..f62e9c23ad3 100644 --- a/apps/sim/app/(auth)/components/password-input.tsx +++ b/apps/sim/app/(auth)/components/password-input.tsx @@ -2,7 +2,7 @@ import { useState } from 'react' import { ChipInput, type ChipInputProps, cn } from '@sim/emcn' -import { Eye, EyeOff } from 'lucide-react' +import { Eye, EyeOff } from '@sim/emcn/icons' import { AUTH_CONTROL_HEIGHT } from '@/app/(auth)/components/constants' type PasswordInputProps = Omit diff --git a/apps/sim/app/(auth)/components/social-login-buttons.tsx b/apps/sim/app/(auth)/components/social-login-buttons.tsx index f315e52da97..c2156e7dd3f 100644 --- a/apps/sim/app/(auth)/components/social-login-buttons.tsx +++ b/apps/sim/app/(auth)/components/social-login-buttons.tsx @@ -73,7 +73,6 @@ export function SocialLoginButtons({ const githubButton = ( - {showPassword ? : } + {showPassword ? ( + + ) : ( + + )}
= 15} className='size-[28px] rounded-full p-0' > - + @@ -313,7 +313,7 @@ export const ChatInput: React.FC<{ disabled={isStreaming} className='size-[28px] rounded-full p-0' > - + @@ -344,7 +344,7 @@ export const ChatInput: React.FC<{ disabled={!canSubmit} className='size-[28px] rounded-full p-0' > - + )}
diff --git a/apps/sim/app/(interfaces)/chat/components/input/voice-input.tsx b/apps/sim/app/(interfaces)/chat/components/input/voice-input.tsx index 675c2fb4a41..fc37092c9db 100644 --- a/apps/sim/app/(interfaces)/chat/components/input/voice-input.tsx +++ b/apps/sim/app/(interfaces)/chat/components/input/voice-input.tsx @@ -1,8 +1,8 @@ 'use client' import { useCallback } from 'react' +import { Mic } from '@sim/emcn/icons' import { domAnimation, LazyMotion, m } from 'framer-motion' -import { Mic } from 'lucide-react' interface VoiceInputProps { onVoiceStart: () => void @@ -35,7 +35,7 @@ export function VoiceInput({ }`} title='Start voice conversation' > - + ) } @@ -57,7 +57,7 @@ export function VoiceInput({ whileTap={{ scale: 0.95 }} title='Start voice conversation' > - +
@@ -80,8 +80,8 @@ export function VoiceInput({ whileTap={{ scale: 0.95 }} title='Start voice conversation' > - - + +
diff --git a/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx b/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx index 576ba3f3905..2ac07bad2c0 100644 --- a/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message-container/message-container.tsx @@ -2,7 +2,7 @@ import { memo, type RefObject } from 'react' import { Button } from '@sim/emcn' -import { ArrowDown } from 'lucide-react' +import { ArrowDown } from '@sim/emcn/icons' import { type ChatMessage, ClientChatMessage, diff --git a/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx b/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx index e42b8e42b2b..56e94aae24d 100644 --- a/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message/components/file-download.tsx @@ -2,9 +2,9 @@ import { useState } from 'react' import { Button, cn, Download, Loader } from '@sim/emcn' +import { Music } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { sleep } from '@sim/utils/helpers' -import { Music } from 'lucide-react' import { DefaultFileIcon, getDocumentIcon } from '@/components/icons/document-icons' import { isSafeHttpUrl } from '@/lib/core/utils/urls' import type { ChatFile } from '@/app/(interfaces)/chat/components/message/message' diff --git a/apps/sim/app/(interfaces)/chat/components/message/message.tsx b/apps/sim/app/(interfaces)/chat/components/message/message.tsx index fe7e940bee6..10a0c0bbc2a 100644 --- a/apps/sim/app/(interfaces)/chat/components/message/message.tsx +++ b/apps/sim/app/(interfaces)/chat/components/message/message.tsx @@ -2,7 +2,7 @@ import { memo, useState } from 'react' import { Button, cn, Duplicate, Tooltip } from '@sim/emcn' -import { Check, File as FileIcon, FileText, Image as ImageIcon } from 'lucide-react' +import { Check, File as FileIcon, FileText, ImageUp as ImageIcon } from '@sim/emcn/icons' import { AgentStreamThinkingChrome, AgentStreamToolCallsChrome, @@ -288,7 +288,7 @@ export const ClientChatMessage = memo( }} > {isCopied ? ( - + ) : ( )} diff --git a/apps/sim/app/(interfaces)/chat/components/voice-interface/voice-interface.tsx b/apps/sim/app/(interfaces)/chat/components/voice-interface/voice-interface.tsx index 242ea8bdff1..3cbe29006ff 100644 --- a/apps/sim/app/(interfaces)/chat/components/voice-interface/voice-interface.tsx +++ b/apps/sim/app/(interfaces)/chat/components/voice-interface/voice-interface.tsx @@ -2,8 +2,8 @@ import { type RefObject, useCallback, useEffect, useRef, useState } from 'react' import { Button, cn } from '@sim/emcn' +import { Mic, MicOff, Phone } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { Mic, MicOff, Phone } from 'lucide-react' import dynamic from 'next/dynamic' import { requestJson } from '@/lib/api/client/request' import { speechTokenContract } from '@/lib/api/contracts/media/speech' diff --git a/apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx b/apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx index 51de190af68..c7cc61cff7d 100644 --- a/apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx +++ b/apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx @@ -18,9 +18,9 @@ import { TableRow, Tooltip, } from '@sim/emcn' +import { RefreshCw } from '@sim/emcn/icons' import { formatDateTime } from '@sim/utils/formatting' import { useQueryClient } from '@tanstack/react-query' -import { RefreshCw } from 'lucide-react' import { useRouter } from 'next/navigation' import { type PauseContextDetail, diff --git a/apps/sim/app/(landing)/changelog/components/changelog-actions/changelog-actions.tsx b/apps/sim/app/(landing)/changelog/components/changelog-actions/changelog-actions.tsx index 8bfb499b774..d5713c641f9 100644 --- a/apps/sim/app/(landing)/changelog/components/changelog-actions/changelog-actions.tsx +++ b/apps/sim/app/(landing)/changelog/components/changelog-actions/changelog-actions.tsx @@ -1,7 +1,7 @@ 'use client' import { ChipLink } from '@sim/emcn' -import { BookOpen, Rss } from 'lucide-react' +import { BookOpen, Rss } from '@sim/emcn/icons' import { GithubOutlineIcon } from '@/components/icons' /** diff --git a/apps/sim/app/(landing)/changelog/components/changelog-timeline/changelog-timeline.tsx b/apps/sim/app/(landing)/changelog/components/changelog-timeline/changelog-timeline.tsx index a806a28bb8c..030613e1a52 100644 --- a/apps/sim/app/(landing)/changelog/components/changelog-timeline/changelog-timeline.tsx +++ b/apps/sim/app/(landing)/changelog/components/changelog-timeline/changelog-timeline.tsx @@ -210,7 +210,7 @@ export function ChangelogTimeline({ initialEntries }: ChangelogTimelineProps) { {!done ? (
- + {loading ? 'Loading…' : 'Show more'}
diff --git a/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx b/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx index b0b7aa127d3..f7a7872c093 100644 --- a/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx +++ b/apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx @@ -10,8 +10,8 @@ import { ModalTitle, ModalTrigger, } from '@sim/emcn' +import { X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { X } from 'lucide-react' import Image from 'next/image' import { useRouter } from 'next/navigation' import { GithubIcon, GoogleIcon, MicrosoftIcon } from '@/components/icons' diff --git a/apps/sim/app/(landing)/components/features/components/build-callout/components/build-chat-animation/build-chat-animation.tsx b/apps/sim/app/(landing)/components/features/components/build-callout/components/build-chat-animation/build-chat-animation.tsx index d8334668478..d17b94f807f 100644 --- a/apps/sim/app/(landing)/components/features/components/build-callout/components/build-chat-animation/build-chat-animation.tsx +++ b/apps/sim/app/(landing)/components/features/components/build-callout/components/build-chat-animation/build-chat-animation.tsx @@ -2,8 +2,7 @@ import { useEffect, useRef, useState } from 'react' import { cn } from '@sim/emcn' -import { Blimp } from '@sim/emcn/icons' -import { ArrowUp, Mic, Paperclip, Plus, Slash } from 'lucide-react' +import { ArrowUp, Blimp, Mic, Paperclip, Plus, Slash } from '@sim/emcn/icons' import { ThinkingLoader } from '@/components/ui' const PROMPT = 'Build a workflow to schedule and publish posts to my X account.' diff --git a/apps/sim/app/(landing)/components/features/components/feature-card/feature-card.tsx b/apps/sim/app/(landing)/components/features/components/feature-card/feature-card.tsx index cea43fd45f3..5b7de3ac577 100644 --- a/apps/sim/app/(landing)/components/features/components/feature-card/feature-card.tsx +++ b/apps/sim/app/(landing)/components/features/components/feature-card/feature-card.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react' import { ChipTag, cn } from '@sim/emcn' -import { ArrowRight } from 'lucide-react' +import { ArrowRight } from '@sim/emcn/icons' import Image from 'next/image' import Link from 'next/link' diff --git a/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx b/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx index 6f430ec2660..df3fcb2c954 100644 --- a/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx +++ b/apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx @@ -5,14 +5,14 @@ import { cn, Tooltip } from '@sim/emcn' import { ArrowRight, ArrowUp, - Copy, + Duplicate, Mic, Paperclip, Plus, Slash, ThumbsDown, ThumbsUp, -} from 'lucide-react' +} from '@sim/emcn/icons' import { ThinkingLoader } from '@/components/ui' import { HERO_TOOLTIP_OFFSET } from '@/app/(landing)/components/hero/components/hero-platform-loop/sidebar-hotspots' @@ -167,7 +167,7 @@ export function HeroChatLoop({ phase, fading }: HeroChatLoopProps) {
- +
diff --git a/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-home.tsx b/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-home.tsx index f43dd49f1cd..77fb19f7038 100644 --- a/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-home.tsx +++ b/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-home.tsx @@ -303,9 +303,7 @@ export function StageHome({ )} > {isEmpty ? ( - - Ask Sim to build an agent… - + Ask Sim to build an agent… ) : ( <> diff --git a/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-kb.tsx b/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-kb.tsx index 2526c402e9e..82c183cfb3c 100644 --- a/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-kb.tsx +++ b/apps/sim/app/(landing)/components/hero/components/hero-visual/stage-kb.tsx @@ -1,6 +1,6 @@ import type { CSSProperties, RefObject } from 'react' import { cn } from '@sim/emcn' -import { Upload, X } from 'lucide-react' +import { Upload, X } from '@sim/emcn/icons' import { GRAPH_EDGES, GRAPH_NODES, diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-chat/chat-input.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-chat/chat-input.tsx index 9d1fb6a09d0..7ce8c3c545f 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-chat/chat-input.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-chat/chat-input.tsx @@ -63,7 +63,7 @@ export function LandingPreviewChatInput({ aria-label={placeholder} rows={1} readOnly={readOnly} - className='m-0 block max-h-[200px] min-h-[24px] w-full resize-none overflow-y-auto border-0 bg-transparent px-1 py-1 font-body text-[15px] text-[var(--text-primary)] leading-[24px] tracking-[-0.015em] outline-none placeholder:font-[380] placeholder:text-[var(--text-muted)] focus-visible:ring-0' + className='m-0 block max-h-[200px] min-h-[24px] w-full resize-none overflow-y-auto border-0 bg-transparent px-1 py-1 font-body text-[15px] text-[var(--text-primary)] leading-[24px] tracking-[-0.015em] outline-none placeholder:text-[var(--text-muted)] focus-visible:ring-0' style={{ caretColor: caretHidden ? 'transparent' : 'var(--text-primary)' }} /> diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx index 12d9bb4693f..d2eabfdd3b7 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx @@ -2,9 +2,8 @@ import { memo, useCallback, useEffect, useRef, useState } from 'react' import { ArrowRight, Blimp, Checkbox, ChevronDown, cn, Shuffle } from '@sim/emcn' -import { TypeBoolean, TypeNumber, TypeText } from '@sim/emcn/icons' +import { Mail, Table, Task, TypeBoolean, TypeNumber, TypeText } from '@sim/emcn/icons' import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion' -import { Mail, MessageSquare, Table } from 'lucide-react' import { captureClientEvent } from '@/lib/posthog/client' import { LandingPreviewChatInput } from '@/app/(landing)/components/landing-preview/components/landing-preview-chat/chat-input' import { LandingPreviewChatTitleBar } from '@/app/(landing)/components/landing-preview/components/landing-preview-chat/chat-title-bar' @@ -15,7 +14,7 @@ import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder' /** Static, greyscale suggestion rows mirroring the workspace SuggestedActions. */ const SUGGESTED_ACTIONS = [ { id: 'crm', label: 'Create a CRM with sample data', icon: Table }, - { id: 'slack', label: 'Summarize my unread Slack messages', icon: MessageSquare }, + { id: 'slack', label: 'Summarize my unread Slack messages', icon: Task }, { id: 'gmail', label: 'Draft replies to my support emails', icon: Mail }, { id: 'tracker', label: 'Build a project tracker table', icon: Table }, ] as const diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx index 400d4c47efa..9dccb9a2c18 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-sidebar/landing-preview-sidebar.tsx @@ -58,9 +58,7 @@ function NavItem({ return (
- - {label} - + {label}
) } @@ -75,9 +73,7 @@ function NavItem({ )} > - - {label} - + {label} ) } @@ -133,12 +129,7 @@ export function LandingPreviewSidebar({ )} > - - Home - + Home
@@ -182,10 +173,7 @@ export function LandingPreviewSidebar({ )} > -
+
{workflow.name}
diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-stage/landing-preview-stage-header.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-stage/landing-preview-stage-header.tsx index ff5d55dbd40..d6ab860810b 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-stage/landing-preview-stage-header.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-stage/landing-preview-stage-header.tsx @@ -1,5 +1,4 @@ -import { X } from '@sim/emcn' -import { PanelRight, Workflow } from 'lucide-react' +import { PanelRight, Workflow, X } from '@sim/emcn/icons' interface LandingPreviewStageHeaderProps { /** The staged resource's display name. */ @@ -11,7 +10,7 @@ interface LandingPreviewStageHeaderProps { * (44px, `px-4`, `gap-1.5`) that sits above the workflow canvas in the "chat * everywhere" layout. There is no tab strip and no Deploy/Run: a workflow's * panel actions are `null` in the real header, so it carries only the staged - * resource's identity - the lucide `Workflow` mark (`size-[14px]`, `--text-icon`) + * resource's identity - the emcn `Workflow` mark (`size-[14px]`, `--text-icon`) * and its name in chip geometry - plus the panel's close + collapse controls on * the right. Aligns to the chat pane's title bar so the two read as one header * row across the split. diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx index 7f1d33beba9..027fa0c6157 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx @@ -2,8 +2,8 @@ import { memo } from 'react' import { Blimp } from '@sim/emcn' +import { Database } from '@sim/emcn/icons' import { domAnimation, LazyMotion, m } from 'framer-motion' -import { Database } from 'lucide-react' import { Handle, type NodeProps, Position } from 'reactflow' import { AgentIcon, diff --git a/apps/sim/app/(landing)/components/navbar/components/mobile-nav/mobile-nav.tsx b/apps/sim/app/(landing)/components/navbar/components/mobile-nav/mobile-nav.tsx index 4790edb28d1..c91b65f72fe 100644 --- a/apps/sim/app/(landing)/components/navbar/components/mobile-nav/mobile-nav.tsx +++ b/apps/sim/app/(landing)/components/navbar/components/mobile-nav/mobile-nav.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { ChipLink, cn } from '@sim/emcn' -import { Menu, X } from 'lucide-react' +import { Menu, X } from '@sim/emcn/icons' import Link from 'next/link' import { GithubOutlineIcon } from '@/components/icons' import { NAV_MENUS } from '@/app/(landing)/components/navbar/components/nav-menu-chip' @@ -161,7 +161,6 @@ export function MobileNav({ stars }: MobileNavProps) { variant='border' href='/login' fullWidth - flush prefetch={false} className='h-[40px] justify-center [&>span]:flex-none' onClick={() => setOpen(false)} @@ -172,7 +171,6 @@ export function MobileNav({ stars }: MobileNavProps) { variant='primary' href={DEMO_HREF} fullWidth - flush className='h-[40px] justify-center [&>span]:flex-none' onClick={() => setOpen(false)} > diff --git a/apps/sim/app/(landing)/components/navbar/components/nav-menu-chip/nav-menu-chip.tsx b/apps/sim/app/(landing)/components/navbar/components/nav-menu-chip/nav-menu-chip.tsx index 3036158a3a4..f0d71450684 100644 --- a/apps/sim/app/(landing)/components/navbar/components/nav-menu-chip/nav-menu-chip.tsx +++ b/apps/sim/app/(landing)/components/navbar/components/nav-menu-chip/nav-menu-chip.tsx @@ -1,7 +1,7 @@ 'use client' import { useState } from 'react' -import { ChipChevronDown, chipContentLabelClass, chipGeometryClass, cn } from '@sim/emcn' +import { ChipChevronDown, chipContentLabelClass, chipVariants, cn } from '@sim/emcn' import { NavMenuItem } from '@/app/(landing)/components/navbar/components/nav-menu-chip/components/nav-menu-item' import type { NavMenu } from '@/app/(landing)/components/navbar/components/nav-menu-chip/types' @@ -68,8 +68,9 @@ export function NavMenuChip({ menu }: NavMenuChipProps) { aria-label={`${label} menu`} onFocus={reArm} className={cn( - chipGeometryClass, - 'mx-0.5 inline-flex cursor-pointer transition-colors hover-hover:bg-[var(--surface-active)]', + chipVariants(), + /* Held open by the wrapper's state, not the button's own hover, so the + panel and its trigger light together. */ 'group-focus-within/navmenu:bg-[var(--surface-active)] group-hover/navmenu:bg-[var(--surface-active)]' )} > diff --git a/apps/sim/app/(landing)/components/navbar/navbar.tsx b/apps/sim/app/(landing)/components/navbar/navbar.tsx index 2cf7458cac6..19a67772f3a 100644 --- a/apps/sim/app/(landing)/components/navbar/navbar.tsx +++ b/apps/sim/app/(landing)/components/navbar/navbar.tsx @@ -19,10 +19,10 @@ import { DEMO_HREF, SIGNUP_HREF } from '@/app/(landing)/constants' * {@link NavbarShell} (which frosts the bar to glass on scroll) are isolated * client leaves, so the wordmark and links stay zero-hydration, crawlable HTML. * - * Every item is a bare emcn chip. Both clusters use `gap-1`, which with - * the chips' own `mx-0.5` margins yields 8px between pills; the nav's - * `gap-3.5` (14px) plus the first chip's 2px margin puts exactly 16px - - * twice the inter-chip gap - between the wordmark and the first menu chip. + * Every item is a bare emcn chip. Chips carry no margin of their own, so both + * clusters' `gap-1` is the full 4px between pills, and the nav's own `gap-4` + * is the full 16px between the wordmark and the first menu chip - twice the + * inter-chip gap. Only that first gap is live: the trailing cluster is `ml-auto`. * Horizontal padding (`px-20`, 48px) matches every section's edge gutter, * and the bar content is capped and centered at the shared * `max-w-[1460px]` (1300px content + the two 80px gutters) so the wordmark @@ -59,7 +59,7 @@ export function Navbar({ stars, logoOnly = false }: NavbarProps) { aria-label='Primary navigation' itemScope itemType='https://schema.org/SiteNavigationElement' - className='relative mx-auto flex w-full max-w-[1460px] items-center gap-3.5 px-20 py-4 max-sm:px-5 max-lg:px-8' + className='relative mx-auto flex w-full max-w-[1460px] items-center gap-4 px-20 py-4 max-sm:px-5 max-lg:px-8' > diff --git a/apps/sim/app/(landing)/components/share-button/share-button.tsx b/apps/sim/app/(landing)/components/share-button/share-button.tsx index 8d0bb6dc095..b95f1ee81ba 100644 --- a/apps/sim/app/(landing)/components/share-button/share-button.tsx +++ b/apps/sim/app/(landing)/components/share-button/share-button.tsx @@ -9,8 +9,7 @@ import { TRIGGER_BORDER_CLASS, useCopyToClipboard, } from '@sim/emcn' -import { Duplicate } from '@sim/emcn/icons' -import { Share2 } from 'lucide-react' +import { Duplicate, Share } from '@sim/emcn/icons' import { LinkedInIcon, xIcon as XIcon } from '@/components/icons' interface ShareButtonProps { @@ -35,7 +34,7 @@ export function ShareButton({ url, title }: ShareButtonProps) { return ( - + Share diff --git a/apps/sim/app/(landing)/components/solutions-page/components/solutions-card-row/components/solutions-pill-cta/solutions-pill-cta.tsx b/apps/sim/app/(landing)/components/solutions-page/components/solutions-card-row/components/solutions-pill-cta/solutions-pill-cta.tsx index 05e80ee7122..48935402b55 100644 --- a/apps/sim/app/(landing)/components/solutions-page/components/solutions-card-row/components/solutions-pill-cta/solutions-pill-cta.tsx +++ b/apps/sim/app/(landing)/components/solutions-page/components/solutions-card-row/components/solutions-pill-cta/solutions-pill-cta.tsx @@ -1,7 +1,7 @@ 'use client' import { ChipLink } from '@sim/emcn' -import { ArrowRight } from 'lucide-react' +import { ArrowRight } from '@sim/emcn/icons' import type { SolutionsPillCta as SolutionsPillCtaConfig } from '@/app/(landing)/components/solutions-page/types' /** diff --git a/apps/sim/app/(landing)/contact/components/contact-form/contact-form.tsx b/apps/sim/app/(landing)/contact/components/contact-form/contact-form.tsx index 28f8d516ee5..b21f950b3bb 100644 --- a/apps/sim/app/(landing)/contact/components/contact-form/contact-form.tsx +++ b/apps/sim/app/(landing)/contact/components/contact-form/contact-form.tsx @@ -284,7 +284,6 @@ export function ContactForm() { updateField('topic', value as ContactRequestPayload['topic'])} @@ -342,7 +341,6 @@ export function ContactForm() { } ) : ( - {placeholder} + {placeholder} )} @@ -275,7 +275,7 @@ export function EnterpriseHomeStage({
- Send message to Sim + Send message to Sim
diff --git a/apps/sim/app/(landing)/enterprise/components/enterprise-platform-loop/enterprise-sidebar.tsx b/apps/sim/app/(landing)/enterprise/components/enterprise-platform-loop/enterprise-sidebar.tsx index c77deff231c..24eddcfc205 100644 --- a/apps/sim/app/(landing)/enterprise/components/enterprise-platform-loop/enterprise-sidebar.tsx +++ b/apps/sim/app/(landing)/enterprise/components/enterprise-platform-loop/enterprise-sidebar.tsx @@ -43,7 +43,7 @@ function IconRow({ icon: Icon, label, active = false }: IconRowProps) { )} > - {label} + {label}
) } @@ -52,7 +52,7 @@ function IconRow({ icon: Icon, label, active = false }: IconRowProps) { function TextRow({ label }: { label: string }) { return (
- {label} + {label}
) } diff --git a/apps/sim/app/(landing)/pricing/components/pricing-card/pricing-card.tsx b/apps/sim/app/(landing)/pricing/components/pricing-card/pricing-card.tsx index de190be948f..31c25d177bd 100644 --- a/apps/sim/app/(landing)/pricing/components/pricing-card/pricing-card.tsx +++ b/apps/sim/app/(landing)/pricing/components/pricing-card/pricing-card.tsx @@ -102,13 +102,7 @@ export function PricingCard({

{priceSubtext ?? ' '}

- + {cta.label}
diff --git a/apps/sim/app/(landing)/scheduled-tasks/components/scheduled-tasks-calendar-loop/scheduled-tasks-calendar-loop.tsx b/apps/sim/app/(landing)/scheduled-tasks/components/scheduled-tasks-calendar-loop/scheduled-tasks-calendar-loop.tsx index 73824540eae..a04aa831627 100644 --- a/apps/sim/app/(landing)/scheduled-tasks/components/scheduled-tasks-calendar-loop/scheduled-tasks-calendar-loop.tsx +++ b/apps/sim/app/(landing)/scheduled-tasks/components/scheduled-tasks-calendar-loop/scheduled-tasks-calendar-loop.tsx @@ -2,8 +2,7 @@ import { useState } from 'react' import { Chip, chipContentGap, chipPrimaryFillTokens, cn } from '@sim/emcn' -import { Calendar, Plus } from '@sim/emcn/icons' -import { ChevronLeft, ChevronRight } from 'lucide-react' +import { Calendar, ChevronLeft, ChevronRight, Plus } from '@sim/emcn/icons' import { HeroLoopShell } from '@/app/(landing)/components/shared/hero-loop-shell' import { RESET_FADE_MS } from '@/app/(landing)/hooks/use-design-scale' import { useMotionSafeCycle } from '@/app/(landing)/hooks/use-motion-safe-cycle' @@ -175,11 +174,11 @@ function ScheduledTasksCalendarPane({ stampedCount }: ScheduledTasksCalendarPane
-
+
Today {MONTH_LABEL}
-
+
Month diff --git a/apps/sim/app/_styles/globals.css b/apps/sim/app/_styles/globals.css index 8db3d1dc639..e6678aed470 100644 --- a/apps/sim/app/_styles/globals.css +++ b/apps/sim/app/_styles/globals.css @@ -12,7 +12,7 @@ :root { --sidebar-width: 0px; /* 0 outside workspace; blocking script always sets actual value on workspace pages */ --sidebar-collapsed-width: 51px; /* icon rail on web; desktop overrides to 0 before first paint */ - --sidebar-expanded-width: 248px; /* SIDEBAR_WIDTH.DEFAULT; the width to restore to, held even while collapsed */ + --sidebar-expanded-width: 238px; /* SIDEBAR_WIDTH.DEFAULT; the width to restore to, held even while collapsed */ --desktop-title-bar-height: 0px; /* macOS traffic-light lane; desktop overrides before first paint */ --workspace-content-title-bar-inset: 0px; /* lane the content pane must leave clear; only non-zero when the pane, not the sidebar, sits under it */ --desktop-title-bar-inset-x: 0px; /* clearance past the traffic lights; desktop overrides */ @@ -54,6 +54,14 @@ --shadow-kbd: 0 4px 0 0 rgba(48, 48, 48, 1); --shadow-kbd-sm: 0 2px 0 0 rgba(48, 48, 48, 1); --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.04); + /** + * The scale's around-shape entry: one layer like every other, borrowing + * `--shadow-subtle`'s alpha and `--shadow-medium`'s blur, with the y-offset + * dropped so the spread lands evenly on all four sides instead of pooling + * underneath. For a raised surface that floats in open space (the chat input); + * the directional entries above still own menus, cards, and overlays. + */ + --shadow-ambient: 0 0 12px 0 rgba(0, 0, 0, 0.08); } /** @@ -342,10 +350,11 @@ html.sidebar-booting .sidebar-shell-inner { --text-primary: #1a1a1a; --text-secondary: #525252; --text-tertiary: #5c5c5c; - --text-muted: #707070; + --text-muted: #7a7a7a; --text-subtle: #8c8c8c; - --text-body: #3b3b3b; - --text-icon: #525252; + --text-body: #434343; + --text-icon: #5a5a5a; + --text-icon-muted: #5c5c5c; --text-inverse: #ffffff; --text-muted-inverse: #a0a0a0; @@ -379,11 +388,6 @@ html.sidebar-booting .sidebar-shell-inner { /* Utility */ --white: #ffffff; - /* Font weights - lighter for light mode */ - --font-weight-base: 420; - --font-weight-medium: 440; - --font-weight-semibold: 500; - /* Landing / marketing - text (static dark-section colors) */ --landing-text: #ececec; --landing-text-muted: #999999; @@ -501,10 +505,11 @@ html.sidebar-booting .sidebar-shell-inner { --text-primary: #e6e6e6; --text-secondary: #cccccc; --text-tertiary: #b3b3b3; - --text-muted: #787878; + --text-muted: #6e6e6e; --text-subtle: #7d7d7d; - --text-body: #cdcdcd; - --text-icon: #a0a0a0; + --text-body: #c1c1c1; + --text-icon: #969696; + --text-icon-muted: #949494; --text-inverse: #1b1b1b; --text-muted-inverse: #b3b3b3; @@ -538,11 +543,6 @@ html.sidebar-booting .sidebar-shell-inner { /* Utility */ --white: #ffffff; - /* Font weights - standard weights for dark mode */ - --font-weight-base: 420; - --font-weight-medium: 480; - --font-weight-semibold: 550; - /* Code editor (dark mode overrides) */ --code-bg: #1f1f1f; --code-foreground: #eeeeee; @@ -618,6 +618,8 @@ html.sidebar-booting .sidebar-shell-inner { /* Shadow scale - dark */ --shadow-overlay: 0 10px 30px rgba(0, 0, 0, 0.3); + /* Same 2.7x light-to-dark alpha step --shadow-overlay takes (0.11 -> 0.3). */ + --shadow-ambient: 0 0 12px 0 rgba(0, 0, 0, 0.22); } } diff --git a/apps/sim/app/api/workflows/[id]/references/route.test.ts b/apps/sim/app/api/workflows/[id]/references/route.test.ts deleted file mode 100644 index 785a1ec2893..00000000000 --- a/apps/sim/app/api/workflows/[id]/references/route.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @vitest-environment node - */ -import { authMockFns, createMockRequest, workflowAuthzMockFns } from '@sim/testing' -import { beforeEach, describe, expect, it, vi } from 'vitest' - -const { mockGetWorkflowReferences } = vi.hoisted(() => ({ - mockGetWorkflowReferences: vi.fn(), -})) - -const mockAuthorizeWorkflow = workflowAuthzMockFns.mockAuthorizeWorkflowByWorkspacePermission - -vi.mock('@/lib/workflows/references/operations', () => ({ - getWorkflowReferences: mockGetWorkflowReferences, -})) - -import { GET } from '@/app/api/workflows/[id]/references/route' - -const mockGetSession = authMockFns.mockGetSession - -const REFERENCES = { - callers: [{ id: 'b', name: 'B', cycle: false, children: [] }], - callees: [], -} - -function callRoute(id = 'wf-1') { - const url = `http://localhost:3000/api/workflows/${id}/references` - return GET(createMockRequest('GET', undefined, {}, url), { params: Promise.resolve({ id }) }) -} - -describe('GET /api/workflows/[id]/references', () => { - beforeEach(() => { - vi.clearAllMocks() - mockGetSession.mockResolvedValue({ user: { id: 'user-1' } }) - mockAuthorizeWorkflow.mockResolvedValue({ - allowed: true, - status: 200, - workflow: { id: 'wf-1', workspaceId: 'ws-1' }, - workspacePermission: 'read', - }) - mockGetWorkflowReferences.mockResolvedValue(REFERENCES) - }) - - it('returns 401 without a session', async () => { - mockGetSession.mockResolvedValue(null) - const response = await callRoute() - expect(response.status).toBe(401) - expect(mockGetWorkflowReferences).not.toHaveBeenCalled() - }) - - it('returns 404 when the workflow does not exist', async () => { - mockAuthorizeWorkflow.mockResolvedValue({ - allowed: false, - status: 404, - message: 'Workflow not found', - workflow: null, - workspacePermission: null, - }) - const response = await callRoute() - expect(response.status).toBe(404) - expect(mockGetWorkflowReferences).not.toHaveBeenCalled() - }) - - it('returns 403 when the user cannot read the workflow', async () => { - mockAuthorizeWorkflow.mockResolvedValue({ - allowed: false, - status: 403, - message: 'Unauthorized: Access denied to read this workflow', - workflow: { id: 'wf-1', workspaceId: 'ws-1' }, - workspacePermission: null, - }) - const response = await callRoute() - expect(response.status).toBe(403) - expect(mockGetWorkflowReferences).not.toHaveBeenCalled() - }) - - it('returns the reference trees scoped to the workflow workspace', async () => { - const response = await callRoute() - expect(response.status).toBe(200) - expect(await response.json()).toEqual(REFERENCES) - expect(mockAuthorizeWorkflow).toHaveBeenCalledWith({ - workflowId: 'wf-1', - userId: 'user-1', - action: 'read', - }) - expect(mockGetWorkflowReferences).toHaveBeenCalledWith('ws-1', 'wf-1') - }) -}) diff --git a/apps/sim/app/api/workflows/[id]/references/route.ts b/apps/sim/app/api/workflows/[id]/references/route.ts deleted file mode 100644 index c2e5505e391..00000000000 --- a/apps/sim/app/api/workflows/[id]/references/route.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { authorizeWorkflowByWorkspacePermission } from '@sim/platform-authz/workflow' -import type { NextRequest } from 'next/server' -import { NextResponse } from 'next/server' -import { getWorkflowReferencesContract } from '@/lib/api/contracts/workflow-references' -import { parseRequest } from '@/lib/api/server' -import { getSession } from '@/lib/auth' -import { withRouteHandler } from '@/lib/core/utils/with-route-handler' -import { getWorkflowReferences } from '@/lib/workflows/references/operations' - -type RouteContext = { params: Promise<{ id: string }> } - -export const GET = withRouteHandler(async (request: NextRequest, context: RouteContext) => { - const session = await getSession() - if (!session?.user?.id) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - - const parsed = await parseRequest(getWorkflowReferencesContract, request, context) - if (!parsed.success) return parsed.response - - const { id } = parsed.data.params - - const auth = await authorizeWorkflowByWorkspacePermission({ - workflowId: id, - userId: session.user.id, - action: 'read', - }) - if (!auth.allowed || !auth.workflow?.workspaceId) { - return NextResponse.json( - { error: auth.message ?? 'Access denied' }, - { status: auth.allowed ? 403 : auth.status } - ) - } - - const references = await getWorkflowReferences(auth.workflow.workspaceId, id) - return NextResponse.json(references) -}) diff --git a/apps/sim/app/f/[token]/public-file-auth.tsx b/apps/sim/app/f/[token]/public-file-auth.tsx index 005f8606248..c0682b0d123 100644 --- a/apps/sim/app/f/[token]/public-file-auth.tsx +++ b/apps/sim/app/f/[token]/public-file-auth.tsx @@ -2,8 +2,8 @@ import { useState } from 'react' import { cn, Input, Label } from '@sim/emcn' +import { Eye, EyeOff } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' -import { Eye, EyeOff } from 'lucide-react' import { useRouter } from 'next/navigation' import { AuthSubmitButton } from '@/app/(auth)/components' import { PublicFileAuthShell } from '@/app/f/[token]/public-file-auth-shell' @@ -75,7 +75,7 @@ export function PublicFileAuth({ token }: PublicFileAuthProps) { className='-translate-y-1/2 absolute top-1/2 right-3 text-[var(--text-muted)] hover:text-[var(--text-primary)]' aria-label={showPassword ? 'Hide password' : 'Show password'} > - {showPassword ? : } + {showPassword ? : }
{error ?

{error}

: null} diff --git a/apps/sim/app/invite/components/status-card.tsx b/apps/sim/app/invite/components/status-card.tsx index 67ec787d8ce..fb0b1e6d433 100644 --- a/apps/sim/app/invite/components/status-card.tsx +++ b/apps/sim/app/invite/components/status-card.tsx @@ -34,10 +34,10 @@ export function InviteStatusCard({ return ( <>
-

+

Loading

-

{description}

+

{description}

@@ -49,10 +49,10 @@ export function InviteStatusCard({ return ( <>
-

+

{title}

-

{description}

+

{description}

@@ -78,7 +78,6 @@ export function InviteStatusCard({ = MIN instead of a sub-minimum sliver. - var defaultSidebarWidth = 248; + var defaultSidebarWidth = 238; try { // Collapse comes from the cookie (independent of localStorage // parsing); the persisted width is read defensively below. Match the @@ -116,10 +116,10 @@ export default function RootLayout({ children }: { children: React.ReactNode }) // collapsed, because the desktop hover-peek renders the sidebar at // its restore width while --sidebar-width still reads collapsed. var width = state && state.sidebarWidth; - var maxSidebarWidth = Math.max(248, window.innerWidth * 0.3); + var maxSidebarWidth = Math.max(238, window.innerWidth * 0.3); var expandedWidth = typeof width === 'number' && isFinite(width) - ? Math.min(Math.max(width, 248), maxSidebarWidth) + ? Math.min(Math.max(width, 238), maxSidebarWidth) : defaultSidebarWidth; document.documentElement.style.setProperty( '--sidebar-expanded-width', diff --git a/apps/sim/app/playground/page.tsx b/apps/sim/app/playground/page.tsx index 4923394efa3..10560fa90da 100644 --- a/apps/sim/app/playground/page.tsx +++ b/apps/sim/app/playground/page.tsx @@ -77,14 +77,13 @@ import { ToastProvider, Tooltip, Trash, - Trash2, toast, Undo, Wrap, ZoomIn, ZoomOut, } from '@sim/emcn' -import { ArrowLeft, Folder, Moon, Sun } from 'lucide-react' +import { ArrowLeft, Folder, Moon, Sun } from '@sim/emcn/icons' import { notFound, useRouter } from 'next/navigation' import { env, isTruthy } from '@/lib/core/config/env' @@ -1050,7 +1049,6 @@ export default function PlaygroundPage() { { Icon: Redo, name: 'Redo' }, { Icon: Rocket, name: 'Rocket' }, { Icon: Trash, name: 'Trash' }, - { Icon: Trash2, name: 'Trash2' }, { Icon: Undo, name: 'Undo' }, { Icon: Wrap, name: 'Wrap' }, { Icon: ZoomIn, name: 'ZoomIn' }, diff --git a/apps/sim/app/unsubscribe/unsubscribe.tsx b/apps/sim/app/unsubscribe/unsubscribe.tsx index 605f45a1943..5f3483accb1 100644 --- a/apps/sim/app/unsubscribe/unsubscribe.tsx +++ b/apps/sim/app/unsubscribe/unsubscribe.tsx @@ -43,9 +43,7 @@ function UnsubscribeContent() {

Loading

-

- Validating your unsubscribe link… -

+

Validating your unsubscribe link…

@@ -61,7 +59,7 @@ function UnsubscribeContent() {

Invalid Unsubscribe Link

-

{error}

+

{error}

@@ -80,7 +78,7 @@ function UnsubscribeContent() {

Important Account Emails

-

+

Transactional emails like password resets, account confirmations, and security alerts cannot be unsubscribed from as they contain essential information for your account.

@@ -102,7 +100,7 @@ function UnsubscribeContent() {

Successfully Unsubscribed

-

+

You have been unsubscribed from our emails. You will stop receiving emails within 48 hours.

@@ -125,10 +123,10 @@ function UnsubscribeContent() {

Email Preferences

-

+

Choose which emails you'd like to stop receiving.

-

{data?.email}

+

{data?.email}

@@ -145,14 +143,11 @@ function UnsubscribeContent() {
- - or choose specific types - + or choose specific types
handleUnsubscribe('marketing')} disabled={ processing || @@ -168,7 +163,6 @@ function UnsubscribeContent() { handleUnsubscribe('updates')} disabled={ processing || @@ -184,7 +178,6 @@ function UnsubscribeContent() { handleUnsubscribe('notifications')} disabled={ processing || @@ -200,7 +193,7 @@ function UnsubscribeContent() {
-

+

You'll continue receiving important account emails like password resets and security alerts.

@@ -218,9 +211,7 @@ export default function Unsubscribe() {

Loading

-

- Validating your unsubscribe link… -

+

Validating your unsubscribe link…

diff --git a/apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-detail-layout.tsx b/apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-detail-layout.tsx index d4099a6a6f7..2738285d17b 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-detail-layout.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/credential-detail/components/credential-detail-layout.tsx @@ -21,7 +21,7 @@ export function CredentialDetailLayout({ back, actions, children }: CredentialDe
{back} - {actions ?
{actions}
: null} + {actions ?
{actions}
: null}
{children}
diff --git a/apps/sim/app/workspace/[workspaceId]/components/custom-tool-editor/generate-prompt-control.tsx b/apps/sim/app/workspace/[workspaceId]/components/custom-tool-editor/generate-prompt-control.tsx index a0d07c1e511..a0f290f045d 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/custom-tool-editor/generate-prompt-control.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/custom-tool-editor/generate-prompt-control.tsx @@ -2,7 +2,7 @@ import { useRef, useState } from 'react' import { Chip, ChipInput } from '@sim/emcn' -import { ArrowUp } from 'lucide-react' +import { ArrowUp } from '@sim/emcn/icons' interface GeneratePromptControlProps { isLoading: boolean @@ -42,7 +42,7 @@ export function GeneratePromptControl({ if (!isActive) { return ( - + Generate ) @@ -72,7 +72,6 @@ export function GeneratePromptControl({ placeholder='Describe what to generate...' />
- {icon ?? } + {icon ?? }
-

+

{title}

diff --git a/apps/sim/app/workspace/[workspaceId]/components/index.ts b/apps/sim/app/workspace/[workspaceId]/components/index.ts index 02855214e23..16570ad0070 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/components/index.ts @@ -2,6 +2,7 @@ export { ConversationListItem } from './conversation-list-item' export type { ErrorBoundaryProps, ErrorStateProps } from './error' export { ErrorShell, ErrorState } from './error' export { InlineRenameInput } from './inline-rename-input' +export { IntegrationTabsHeader } from './integration-tabs-header' export { MessageActions } from './message-actions' export { FloatingOverflowText } from './resource/components/floating-overflow-text' export { ownerCell } from './resource/components/owner-cell' diff --git a/apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/index.ts b/apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/index.ts new file mode 100644 index 00000000000..1706152ecf3 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/index.ts @@ -0,0 +1 @@ +export { IntegrationTabsHeader } from './integration-tabs-header' diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/integration-tabs-header.tsx b/apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/integration-tabs-header.tsx similarity index 52% rename from apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/integration-tabs-header.tsx rename to apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/integration-tabs-header.tsx index b4d71e20bab..5306bc87d6c 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/integration-tabs-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/integration-tabs-header/integration-tabs-header.tsx @@ -1,17 +1,24 @@ import type { ReactNode } from 'react' -import { ChipLink } from '@sim/emcn' +import { ChipLink, cn } from '@sim/emcn' import { PAGE_HEADER_BAR } from '@/components/page-header-bar' interface IntegrationTabsHeaderProps { active: 'integrations' | 'skills' workspaceId: string + /** Trailing actions for the owning page (e.g. skills' "Add skill"). */ rightSlot?: ReactNode } /** - * Top-of-page chip header shared by the Integrations and Skills pages. - * Highlights the active tab and links to the sibling tab; `rightSlot` lets - * each page render its own trailing actions (e.g. an "Add skill" button). + * Top-of-page tab header shared by the Integrations and Skills pages — two halves + * of one surface, so each highlights itself and links to its sibling. + * + * Lives in the shared workspace components rather than under `integrations/` + * because both pages own it equally; its former home made Skills reach across into + * a sibling feature for its own chrome. + * + * The `gap-1` is explicit because chips carry no outer margin — the parent owns the + * space between them. */ export function IntegrationTabsHeader({ active, @@ -19,7 +26,7 @@ export function IntegrationTabsHeader({ rightSlot, }: IntegrationTabsHeaderProps) { return ( -

+
Integrations diff --git a/apps/sim/app/workspace/[workspaceId]/components/invite-modal/invite-modal.tsx b/apps/sim/app/workspace/[workspaceId]/components/invite-modal/invite-modal.tsx index e80ebb4033a..07b4d7bcba7 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/invite-modal/invite-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/invite-modal/invite-modal.tsx @@ -328,7 +328,6 @@ export function InviteModal({ searchable={isOrganizationInvite} searchPlaceholder='Search workspaces...' fullWidth - flush disabled={isSubmitting || !canInvite || workspaceOptions.length <= 1} /> diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx index a28d735a1f8..a66c4b249d0 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-header/resource-header.tsx @@ -29,7 +29,7 @@ import { useFloatingTooltip, useIsOverflowing, } from '@sim/emcn' -import { ArrowUpLeft } from 'lucide-react' +import { ArrowUpLeft } from '@sim/emcn/icons' import { createPortal } from 'react-dom' import { TITLE_BAR_LANE_PT } from '@/components/page-header-bar' import { InlineRenameInput } from '@/app/workspace/[workspaceId]/components/inline-rename-input' @@ -204,7 +204,7 @@ export const ResourceHeader = memo(function ResourceHeader({ )}
{(aside || (actions && actions.length > 0)) && ( -
+
{aside} {actions?.map((action) => ( 0) { return ( @@ -453,7 +450,7 @@ function BreadcrumbLocationPopover({ onMouseEnter={openPopover} onMouseLeave={scheduleClose} className={cn( - chipVariants({ flush: true }), + chipVariants(), 'max-w-none gap-1.5 px-2 transition-colors', open && 'relative z-[var(--z-popover)]', className @@ -461,10 +458,7 @@ function BreadcrumbLocationPopover({ > - + {rootBreadcrumb?.label && ( diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.tsx index de67d5ef176..63d9e25b7e5 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options/resource-options.tsx @@ -143,7 +143,7 @@ export const ResourceOptions = memo(function ResourceOptions({ and only the trailing action is pushed to the far edge. */}
{aside} -
+
{filterTags?.map((tag) => ( {tag.label} @@ -163,7 +163,7 @@ export const ResourceOptions = memo(function ResourceOptions({ } > -
+
Filter diff --git a/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx b/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx index 718617f30bc..721d3bc0c5a 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx @@ -21,9 +21,8 @@ import { cn, Loader, } from '@sim/emcn' -import { Pin } from '@sim/emcn/icons' +import { ChevronLeft, ChevronRight, Pin } from '@sim/emcn/icons' import { useVirtualizer } from '@tanstack/react-virtual' -import { ChevronLeft, ChevronRight } from 'lucide-react' import { InlineRenameInput } from '@/app/workspace/[workspaceId]/components/inline-rename-input' import { FloatingOverflowText } from '@/app/workspace/[workspaceId]/components/resource/components/floating-overflow-text' import { ResourceHeader } from '@/app/workspace/[workspaceId]/components/resource/components/resource-header' diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/editor-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/editor-context-menu.tsx index 904d3c06a11..b98c7e9cbf0 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/editor-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/editor-context-menu.tsx @@ -8,8 +8,7 @@ import { DropdownMenuShortcut, DropdownMenuTrigger, } from '@sim/emcn' -import { Clipboard, Duplicate, Search, SelectAll } from '@sim/emcn/icons' -import { Scissors } from 'lucide-react' +import { Clipboard, Duplicate, Scissors, Search, SelectAll } from '@sim/emcn/icons' interface EditorContextMenuProps { isOpen: boolean diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx index 9984e16b0df..673f02f09b5 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/file-viewer.tsx @@ -1,7 +1,7 @@ 'use client' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Music } from 'lucide-react' +import { Music } from '@sim/emcn/icons' import dynamic from 'next/dynamic' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' import { getFileExtension } from '@/lib/uploads/utils/file-utils' @@ -380,7 +380,7 @@ const MediaPreview = memo(function MediaPreview({ return (
- +

{file.name}

{blobUrl && ( diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-toolbar.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-toolbar.tsx index 0d8a326a881..889fa350bd2 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-toolbar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/preview-toolbar.tsx @@ -1,5 +1,5 @@ import { Chip, cn } from '@sim/emcn' -import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut } from 'lucide-react' +import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut } from '@sim/emcn/icons' interface PreviewNavigationControls { current: number @@ -34,10 +34,10 @@ export function PreviewToolbar({ navigation, zoom, className }: PreviewToolbarPr className )} > -
+
{navigation && }
-
{zoom && }
+
{zoom && }
) } diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/code-block.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/code-block.tsx index 7db9b1481b9..b23fd4a8a58 100644 --- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/code-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/code-block.tsx @@ -8,11 +8,11 @@ import { DropdownMenuTrigger, useCopyToClipboard, } from '@sim/emcn' +import { Check, ChevronDown, Code, Duplicate, Eye, Wrap } from '@sim/emcn/icons' import type { JSONContent } from '@tiptap/core' import { CodeBlock } from '@tiptap/extension-code-block' import type { ReactNodeViewProps } from '@tiptap/react' import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react' -import { Check, ChevronDown, Code, Copy, Eye, WrapText } from 'lucide-react' import { looksLikeMermaid, MermaidDiagram } from '../mermaid-diagram' import { detectLanguage } from './detect-language' import { useEditorEditable } from './use-editor-editable' @@ -155,7 +155,7 @@ function CodeBlockView({ node, updateAttributes, editor, getPos }: ReactNodeView type='button' aria-label='Code language' className={cn( - chipVariants({ variant: 'default', flush: true }), + chipVariants({ variant: 'default' }), 'h-[24px] gap-1 px-1.5 text-[var(--text-muted)] data-[state=open]:bg-[var(--surface-active)] data-[state=open]:text-[var(--text-body)]' )} > @@ -193,7 +193,7 @@ function CodeBlockView({ node, updateAttributes, editor, getPos }: ReactNodeView wrap && 'bg-[var(--surface-active)] text-[var(--text-body)]' )} > - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/mention-list.test.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/mention-list.test.tsx
index 2f5241d11ca..8ff82847a9d 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/mention-list.test.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/mention-list.test.tsx
@@ -7,9 +7,9 @@
  * The second test drives the real `ReactRenderer` path the suggestion plugin actually uses.
  */
 import { act, createRef } from 'react'
+import { File } from '@sim/emcn/icons'
 import { Editor } from '@tiptap/core'
 import { EditorContent, ReactRenderer } from '@tiptap/react'
-import { File } from 'lucide-react'
 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
 import { createMarkdownEditorExtensions } from '../editor-extensions'
 import { MentionList, type MentionListHandle } from './mention-list'
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/types.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/types.ts
index dd87663dcc7..a26eb56869b 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/types.ts
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/mention/types.ts
@@ -24,6 +24,6 @@ export interface MentionItem {
   label: string
   /** Category heading the item is shown under. */
   group: string
-  /** Optional per-item icon (Lucide category icon or a brand block icon). */
+  /** Optional per-item icon (emcn category icon or a brand block icon). */
   icon?: ComponentType<{ className?: string }>
 }
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/bubble-menu.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/bubble-menu.tsx
index 56b4dd78389..2150081da81 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/bubble-menu.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/bubble-menu.tsx
@@ -1,9 +1,4 @@
 import { useCallback, useEffect, useRef, useState } from 'react'
-import { posToDOMRect } from '@tiptap/core'
-import { PluginKey } from '@tiptap/pm/state'
-import type { Editor } from '@tiptap/react'
-import { useEditorState } from '@tiptap/react'
-import { BubbleMenu } from '@tiptap/react/menus'
 import {
   Bold,
   Check,
@@ -19,7 +14,12 @@ import {
   Strikethrough,
   TextQuote,
   Unlink,
-} from 'lucide-react'
+} from '@sim/emcn/icons'
+import { posToDOMRect } from '@tiptap/core'
+import { PluginKey } from '@tiptap/pm/state'
+import type { Editor } from '@tiptap/react'
+import { useEditorState } from '@tiptap/react'
+import { BubbleMenu } from '@tiptap/react/menus'
 import { applyLink, LinkUrlInput } from './link-editing'
 import { ToolbarButton, ToolbarDivider } from './toolbar-button'
 
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/link-hover-card.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/link-hover-card.tsx
index b56094f2e0b..015b61465ea 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/link-hover-card.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/link-hover-card.tsx
@@ -1,9 +1,9 @@
 import { useCallback, useEffect, useRef, useState } from 'react'
 import { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom'
 import { useCopyToClipboard } from '@sim/emcn'
+import { Check, Duplicate, Pencil, Unlink } from '@sim/emcn/icons'
 import { getMarkRange } from '@tiptap/core'
 import type { Editor } from '@tiptap/react'
-import { Check, Copy, Pencil, Unlink } from 'lucide-react'
 import { createPortal } from 'react-dom'
 import { normalizeLinkHref } from '../markdown-fidelity'
 import { applyLink, LinkUrlInput } from './link-editing'
@@ -178,7 +178,7 @@ export function LinkHoverCard({ editor }: LinkHoverCardProps) {
             
           )}
            {
               void copy(rawHref)
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/table-menu.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/table-menu.tsx
index e62fa8fdcb0..4d7073ccd4e 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/table-menu.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/table-menu.tsx
@@ -1,9 +1,4 @@
 import { useCallback, useState } from 'react'
-import { posToDOMRect } from '@tiptap/core'
-import { PluginKey } from '@tiptap/pm/state'
-import type { Editor } from '@tiptap/react'
-import { useEditorState } from '@tiptap/react'
-import { BubbleMenu } from '@tiptap/react/menus'
 import {
   ArrowDown,
   ArrowLeft,
@@ -12,8 +7,13 @@ import {
   Columns3,
   Rows3,
   Table as TableIcon,
-  Trash2,
-} from 'lucide-react'
+  Trash,
+} from '@sim/emcn/icons'
+import { posToDOMRect } from '@tiptap/core'
+import { PluginKey } from '@tiptap/pm/state'
+import type { Editor } from '@tiptap/react'
+import { useEditorState } from '@tiptap/react'
+import { BubbleMenu } from '@tiptap/react/menus'
 import { ToolbarButton, ToolbarDivider } from './toolbar-button'
 
 /** Pins the toolbar to the viewport instead of tracking the (often wide) table as it scrolls horizontally. */
@@ -118,7 +118,7 @@ export function TableBubbleMenu({ editor, scrollContainerRef }: TableBubbleMenuP
         onClick={() => editor.chain().focus().toggleHeaderRow().run()}
       />
        editor.chain().focus().deleteTable().run()}
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/toolbar-button.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/toolbar-button.tsx
index 7802b3197f4..d6fce435fb7 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/toolbar-button.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/toolbar-button.tsx
@@ -1,8 +1,8 @@
+import type { ComponentType, SVGProps } from 'react'
 import { cn, Tooltip } from '@sim/emcn'
-import type { LucideIcon } from 'lucide-react'
 
 interface ToolbarButtonProps {
-  icon: LucideIcon
+  icon: ComponentType>
   label: string
   shortcut?: string
   isActive?: boolean
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css
index 3fcfb93ba84..bd46c223f00 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.css
@@ -4,7 +4,7 @@
   color: var(--text-primary);
   font-family: var(--font-inter);
   font-size: 15px;
-  font-weight: 430;
+  font-weight: 400;
   line-height: 25px;
   letter-spacing: 0;
   overflow-wrap: anywhere;
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/slash-command/commands.ts b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/slash-command/commands.ts
index 5878b352cb8..000a434eba4 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/slash-command/commands.ts
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/slash-command/commands.ts
@@ -1,19 +1,19 @@
-import type { Editor, Range } from '@tiptap/core'
+import type { ComponentType, SVGProps } from 'react'
 import {
-  Code2,
+  Code,
   Heading1,
   Heading2,
   Heading3,
-  Image as ImageIcon,
+  ImageUp as ImageIcon,
   List,
   ListChecks,
   ListOrdered,
-  type LucideIcon,
   Minus,
   Pilcrow,
   Table as TableIcon,
   TextQuote,
-} from 'lucide-react'
+} from '@sim/emcn/icons'
+import type { Editor, Range } from '@tiptap/core'
 
 export interface SlashCommandContext {
   editor: Editor
@@ -33,7 +33,7 @@ export interface SlashCommandItem {
   title: string
   /** Group heading the item is shown under in the menu. */
   group: string
-  icon: LucideIcon
+  icon: ComponentType>
   /** Extra search terms matched against the slash query, beyond the title. */
   aliases: string[]
   /** Keyboard shortcut shown on the right of the item (omitted when there is none). */
@@ -116,7 +116,7 @@ export const SLASH_COMMANDS: readonly SlashCommandItem[] = [
   {
     title: 'Code block',
     group: 'Blocks',
-    icon: Code2,
+    icon: Code,
     aliases: ['codeblock', 'snippet', 'fence'],
     shortcut: '⌘⌥C',
     run: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run(),
diff --git a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/xlsx-preview.tsx b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/xlsx-preview.tsx
index 6e15f70cde2..5ff5c26b3f4 100644
--- a/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/xlsx-preview.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/xlsx-preview.tsx
@@ -115,7 +115,7 @@ export const XlsxPreview = memo(function XlsxPreview({
   return (
     
-
+
{sheetNames.map((name, i) => ( - {/* Chips carry their own `mx-0.5` cluster spacing, so this row sets no gap. */} -
- +
+ {/* 16px icon + 2px + the row's 4px gap = the 22px text inset the sibling + rows below are tuned to (`pl-[22px]`, and `gap-1.5` on an unmargined + icon). Margin and gap add, so this cannot be `mr-1.5`. */} + {preview ? ( @@ -190,7 +192,7 @@ export function ToolPermissionCard({
{showBulkActions && ( -
+
{outstandingIds.length} tools need permission diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx index 1966672184c..17b24c9cd17 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx @@ -40,14 +40,14 @@ const LANG_ALIASES: Record = { const PROSE_CLASSES = cn( 'prose prose-base dark:prose-invert max-w-none', - 'font-[family-name:var(--font-inter)] antialiased break-words font-[430] tracking-[0]', - 'prose-headings:font-[600] prose-headings:tracking-[0] prose-headings:text-[var(--text-primary)]', + 'font-[family-name:var(--font-inter)] antialiased break-words tracking-[0]', + 'prose-headings:font-semibold prose-headings:tracking-[0] prose-headings:text-[var(--text-primary)]', 'prose-headings:mb-3 prose-headings:mt-6 first:prose-headings:mt-0', 'prose-p:text-base prose-p:leading-[25px] prose-p:text-[var(--text-primary)]', 'prose-li:text-base prose-li:leading-[25px] prose-li:text-[var(--text-primary)]', 'prose-li:my-1', 'prose-ul:my-4 prose-ol:my-4', - 'prose-strong:font-[600] prose-strong:text-[var(--text-primary)]', + 'prose-strong:font-semibold prose-strong:text-[var(--text-primary)]', 'prose-a:text-[var(--text-primary)] prose-a:underline prose-a:decoration-dashed prose-a:underline-offset-4', 'prose-hr:border-[var(--border)] prose-hr:my-6', 'prose-table:my-0' @@ -195,7 +195,7 @@ function highlight(code: string, language: string): string { const MARKDOWN_COMPONENTS = { table({ children }: { children?: React.ReactNode }) { return ( -
+
{children}
@@ -209,7 +209,7 @@ const MARKDOWN_COMPONENTS = { return ( {children} @@ -232,7 +232,7 @@ const MARKDOWN_COMPONENTS = { if (!codeString) { return ( -
+        
           {children}
         
) @@ -251,7 +251,7 @@ const MARKDOWN_COMPONENTS = {
         
@@ -349,7 +349,7 @@ const MARKDOWN_COMPONENTS = { }, inlineCode({ children }: { children?: React.ReactNode }) { return ( - + {children} ) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx index 5b0206c90ae..080edad2af7 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/options/options.tsx @@ -15,7 +15,7 @@ export function Options({ items, onSelect }: OptionsProps) { key={item.id} type='button' onClick={() => onSelect?.(item.id)} - className='rounded-full border border-[var(--border)] bg-[var(--bg)] px-3.5 py-1.5 font-[430] font-[family-name:var(--font-inter)] text-[var(--text-primary)] text-sm leading-5 transition-colors hover-hover:bg-[var(--surface-5)]' + className='rounded-full border border-[var(--border)] bg-[var(--bg)] px-3.5 py-1.5 font-[family-name:var(--font-inter)] text-[var(--text-primary)] text-sm leading-5 transition-colors hover-hover:bg-[var(--surface-5)]' > {item.label} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx index 38ebfc3a8f6..9c24e1ef92b 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/special-tags.tsx @@ -1501,7 +1501,7 @@ function SecretInputDisplay({ data }: { data: CredentialTagData }) {
@@ -1881,13 +1881,13 @@ function UsageUpgradeDisplay({ data }: { data: UsageUpgradeTagData }) { {canManageBilling ? ( {buttonLabel} ) : ( -

+

{unavailableMessage}

)} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/components/mothership-chat-skeleton/mothership-chat-skeleton.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/components/mothership-chat-skeleton/mothership-chat-skeleton.tsx index 3e8838e2cee..9b32c21b1dd 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/components/mothership-chat-skeleton/mothership-chat-skeleton.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/components/mothership-chat-skeleton/mothership-chat-skeleton.tsx @@ -2,7 +2,7 @@ import { Skeleton } from '@sim/emcn' const LAYOUT_SKELETON_STYLES = { 'mothership-view': { - content: 'mx-auto max-w-[48rem] space-y-6', + content: 'mx-auto max-w-chat space-y-6', userRow: 'flex flex-col items-end gap-[6px] pt-3', }, 'copilot-view': { diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx index 8be99a12122..a0b2f9bdfe7 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-chat/mothership-chat.tsx @@ -111,14 +111,14 @@ const LAYOUT_STYLES = { 'mothership-view': { scrollContainer: 'mt-[var(--workspace-content-title-bar-inset)] min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-6 pt-4 pb-2 [overflow-anchor:none] [scrollbar-gutter:stable_both-edges]', - sizer: 'relative mx-auto w-full max-w-[48rem]', + sizer: 'relative mx-auto w-full max-w-chat', rowGap: 'pb-6', userRow: 'flex flex-col items-end gap-[6px] pt-3', attachmentWidth: 'max-w-[70%]', userBubble: 'max-w-[70%] overflow-hidden rounded-[16px] bg-[var(--surface-5)] px-3.5 py-2', assistantRow: 'group/msg', footer: 'flex-shrink-0 px-[24px] pb-[16px]', - footerInner: 'mx-auto max-w-[48rem]', + footerInner: 'mx-auto max-w-chat', }, 'copilot-view': { scrollContainer: diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx index 0f5bd7849df..791be00bbac 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx @@ -339,8 +339,9 @@ export function ResourceActions({ workspaceId, resource }: ResourceActionsProps) ) case 'log': return + /* No "open in" action — the scheduled-tasks page was removed, so there is + nowhere to route to. The resource itself still renders inline. */ case 'scheduledtask': - return case 'folder': case 'generic': case 'browser': @@ -878,36 +879,6 @@ function EmbeddedScheduledTask({ scheduleId }: EmbeddedScheduledTaskProps) { ) } -interface EmbeddedScheduledTaskActionsProps { - workspaceId: string -} - -function EmbeddedScheduledTaskActions({ workspaceId }: EmbeddedScheduledTaskActionsProps) { - const router = useRouter() - - const handleOpenScheduledTasks = () => { - router.push(`/workspace/${workspaceId}/scheduled-tasks`) - } - - return ( - - - - - -

Open in scheduled tasks

-
-
- ) -} - interface EmbeddedLogProps { workspaceId: string logId: string diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx index ee45c1a1aa9..76979893694 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx @@ -2,7 +2,7 @@ import { useCallback, useRef, useState } from 'react' import { cn, Tooltip } from '@sim/emcn' -import { ArrowUp, ChevronDown, ChevronRight, Paperclip, Pencil, Trash2, X } from 'lucide-react' +import { ArrowUp, ChevronDown, ChevronRight, Paperclip, Pencil, Trash, X } from '@sim/emcn/icons' import { UserMessageContent } from '@/app/workspace/[workspaceId]/home/components/user-message-content' import type { QueuedMessage } from '@/app/workspace/[workspaceId]/home/types' @@ -182,7 +182,7 @@ export function QueuedMessages({ }} className='rounded-md p-[5px] text-[var(--text-icon)] transition-colors hover-hover:bg-[var(--surface-active)] hover-hover:text-[var(--text-primary)]' > - + diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx index a0d97220f45..70a3c817dc8 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx @@ -1,8 +1,8 @@ 'use client' import { type ComponentType, type CSSProperties, useMemo, useState } from 'react' -import { ArrowRight, ChevronDown, chipVariants, cn, Expandable, ExpandableContent } from '@sim/emcn' -import { Shuffle, Table } from '@sim/emcn/icons' +import { ArrowRight, ChevronDown, cn, Expandable, ExpandableContent } from '@sim/emcn' +import { Table } from '@sim/emcn/icons' import { randomFloat } from '@sim/utils/random' import { stripVersionSuffix } from '@sim/utils/string' import { useParams } from 'next/navigation' @@ -149,7 +149,7 @@ function scoreCandidate(c: Candidate, signals: Signals): number { /** * Weighted sampling without replacement. Each pick's probability is - * proportional to its weight, so shuffles stay fresh while staying relevant. + * proportional to its weight, so the set stays varied while staying relevant. */ function weightedSample(pool: readonly T[], n: number, weightOf: (item: T) => number): T[] { const remaining = pool.map((item) => ({ item, weight: Math.max(weightOf(item), 0) })) @@ -277,8 +277,6 @@ export function SuggestedActions({ onSelectPrompt }: SuggestedActionsProps) { * above it. */ const [animationsEnabled, setAnimationsEnabled] = useState(false) - /** Incremented by the shuffle control to re-roll the weighted sample. */ - const [shuffleNonce, setShuffleNonce] = useState(0) /** * OAuth connect modal target. Setting this opens the modal; setting it back * to `null` (via `onOpenChange(false)`) closes it. Mirrors the local-state @@ -307,16 +305,16 @@ export function SuggestedActions({ onSelectPrompt }: SuggestedActionsProps) { ) /** - * Personalized suggestions, re-sampled whenever signals resolve or the user - * shuffles. Falls back to {@link INITIAL_ACTIONS} until the credential and - * service queries have loaded (and stays there for users with no - * connections, unless they shuffle), so first paint never flashes. + * Personalized suggestions, re-sampled whenever signals resolve. Falls back to + * {@link INITIAL_ACTIONS} until the credential and service queries have loaded + * — and stays there for users with no connections — so first paint never + * flashes. */ const actions = useMemo(() => { const personalized = services.length > 0 && connectedProviders.size > 0 - if (!personalized && shuffleNonce === 0) return INITIAL_ACTIONS + if (!personalized) return INITIAL_ACTIONS return computeActions(services, signals) - }, [connectedProviders, services, signals, shuffleNonce]) + }, [connectedProviders, services, signals]) const handleSelect = (action: Action, position: number) => { captureEvent(posthog, 'suggested_action_clicked', { @@ -335,14 +333,6 @@ export function SuggestedActions({ onSelectPrompt }: SuggestedActionsProps) { if (match) setOAuthTarget(match) } - const handleShuffle = () => { - captureEvent(posthog, 'suggested_actions_shuffled', { - workspace_id: workspaceId, - connected_provider_count: connectedProviders.size, - }) - setShuffleNonce((n) => n + 1) - } - const handleToggleExpanded = () => { captureEvent(posthog, 'suggested_actions_toggled', { workspace_id: workspaceId, @@ -353,41 +343,40 @@ export function SuggestedActions({ onSelectPrompt }: SuggestedActionsProps) { } return ( -
-
- - -
+ /> + - -
+ + {/* 6px, matching a sidebar section header to its first item — both headers + are an 18px box around 12px text, so equal padding reads as equal + distance. Padding an inner wrapper rather than the animated element: + `collapsible-up`/`-down` interpolate height alone, so a margin here + would hold its full value through the close and then vanish on unmount, + snapping the content below up. */} +
{actions.map((action, i) => { const Icon = action.icon return ( diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/attached-files-list/attached-files-list.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/attached-files-list/attached-files-list.tsx index 1d917779162..f4e01792827 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/attached-files-list/attached-files-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/attached-files-list/attached-files-list.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Loader, Tooltip } from '@sim/emcn' -import { X } from 'lucide-react' +import { X } from '@sim/emcn/icons' import { getDocumentIcon } from '@/components/icons/document-icons' import type { AttachedFile } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments' diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts index 29e4baa4ea7..69ff07e5f62 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts @@ -58,12 +58,12 @@ export interface PlusMenuHandle { * Box and typography shared by the textarea and its mirror overlay — both must * produce identical line wrapping so the overlay text sits exactly over the * (transparent) textarea text. The scale is the chat input's native prompt - * scale (`text-[15px]`, `-0.015em` tracking); the task modal's body inherits it + * scale (`text-[14px]`, `-0.015em` tracking); the task modal's body inherits it * so the editor reads the same whether it's the chat input or inside the modal. */ const FIELD_MIRROR_CLASSES = cn( 'm-0 box-border min-h-[24px] w-full break-words [overflow-wrap:anywhere] border-0 bg-transparent', - 'px-1 py-1 font-body text-[15px] leading-[24px] tracking-[-0.015em]' + 'px-1 py-1 font-body text-[14px] leading-[24px] tracking-[-0.015em]' ) /** @@ -75,7 +75,7 @@ export const TEXTAREA_BASE_CLASSES = cn( FIELD_MIRROR_CLASSES, 'block h-auto resize-none overflow-hidden', 'text-transparent caret-[var(--text-primary)] outline-none', - 'placeholder:font-[380] placeholder:text-[var(--text-subtle)]', + 'placeholder:text-[var(--text-muted)]', 'focus-visible:ring-0 focus-visible:ring-offset-0' ) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx index 55d73124605..c9a1d139e63 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/user-input.tsx @@ -512,8 +512,8 @@ const UserInputImpl = forwardRef(function UserI
(function UserI placeholder='Ask Sim to ' onSubmit={handleEnterSubmit} onArrowUpOnEmpty={handleArrowUpOnEmpty} - className={isInitialView ? 'max-h-[30vh]' : 'max-h-[200px]'} + className={isInitialView ? 'h-[56px]' : 'max-h-[200px]'} />
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx index b5bb7ace728..a52852d122e 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx @@ -7,10 +7,10 @@ import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/type import { getIntegrationMatcher } from '@/blocks/integration-matcher' const USER_MESSAGE_CLASSES = - 'whitespace-pre-wrap break-words [overflow-wrap:anywhere] font-[430] font-[family-name:var(--font-inter)] text-base text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased' + 'whitespace-pre-wrap break-words [overflow-wrap:anywhere] font-[family-name:var(--font-inter)] text-base text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased' const COMPACT_CLASSES = - 'truncate text-small leading-[20px] font-[430] font-[family-name:var(--font-inter)] text-[var(--text-primary)] tracking-[0] antialiased' + 'truncate text-small leading-[20px] font-[family-name:var(--font-inter)] text-[var(--text-primary)] tracking-[0] antialiased' interface UserMessageContentProps { content: string diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index 868df0fb460..b18d1843897 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -438,10 +438,10 @@ export function Home({ chatId, userName, userId, tableViewsEnabled }: HomeProps)
{/* Asymmetric padding biases the group up so the full cluster (heading + input + suggestions) sits at the optical center */}
-

+

What should we get done{firstName ? `, ${firstName}` : ''}?

-
+
{skill.description}
{added ? ( - + Added ) : ( - + {pending ? 'Adding...' : 'Add'} )} diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/components/connect-slack-bot-modal/connect-slack-bot-modal.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/components/connect-slack-bot-modal/connect-slack-bot-modal.tsx index 7526064411e..16c9bed5a36 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/components/connect-slack-bot-modal/connect-slack-bot-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/components/connect-slack-bot-modal/connect-slack-bot-modal.tsx @@ -12,10 +12,10 @@ import { SecretInput, Wizard, } from '@sim/emcn' +import { Loader } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' -import { Loader2 } from 'lucide-react' import { SlackIcon } from '@/components/icons' import { getBaseUrl } from '@/lib/core/utils/urls' import { SLACK_CUSTOM_BOT_PROVIDER_ID } from '@/lib/oauth/types' @@ -431,7 +431,7 @@ function StepDone({ pending, created, error, onRetry }: StepDoneProps) { if (pending) { return (
- +

Verifying your bot and connecting…

) diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/index.ts b/apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/index.ts deleted file mode 100644 index 95e6eb0ca45..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/integrations/components/integration-tabs-header/integration-tabs-header' diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/components/showcase-with-explore/showcase-with-explore.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/components/showcase-with-explore/showcase-with-explore.tsx index 23e99adca94..886a36cf0aa 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/components/showcase-with-explore/showcase-with-explore.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/components/showcase-with-explore/showcase-with-explore.tsx @@ -1,7 +1,7 @@ 'use client' import { Chip } from '@sim/emcn' -import { ArrowRight } from 'lucide-react' +import { ArrowRight } from '@sim/emcn/icons' import { useParams, useRouter } from 'next/navigation' import { IntegrationsShowcase } from '@/app/workspace/[workspaceId]/integrations/components/integrations-showcase' import { storeCuratedPrompt } from '@/blocks/integration-matcher' @@ -36,7 +36,7 @@ export function ShowcaseWithExplore({ prompt }: ShowcaseWithExploreProps) { storeCuratedPrompt(prompt) router.push(`/workspace/${workspaceId}/home`) }} - className='absolute right-0 bottom-0 mx-0' + className='absolute right-0 bottom-0' > Explore in chat
diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx index 2f04ac50d33..cc183c0c22d 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx @@ -22,8 +22,8 @@ import { type Integration, resolveCredentialDisplay, } from '@/lib/integrations' +import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/components' import { IntegrationSection } from '@/app/workspace/[workspaceId]/integrations/components/integration-section' -import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/integrations/components/integration-tabs-header' import { IntegrationTile } from '@/app/workspace/[workspaceId]/integrations/components/integrations-showcase' import { ShowcaseWithExplore } from '@/app/workspace/[workspaceId]/integrations/components/showcase-with-explore' import { useScrollRestoration } from '@/app/workspace/[workspaceId]/integrations/hooks/use-scroll-restoration' @@ -305,7 +305,7 @@ export function Integrations() { /> -
setShowConnectorsModal(true)} - className={cn(chipVariants({ variant: 'filled', flush: true }), 'max-w-[180px]')} + className={cn(chipVariants({ variant: 'filled' }), 'max-w-[180px]')} > {connector.status === 'syncing' ? ( @@ -1517,7 +1515,6 @@ function TagFilterValueControl({ entry, onChange }: TagFilterValueControlProps) onChange={(value) => onChange({ value })} placeholder='From' fullWidth - flush /> to onChange({ valueTo: value })} placeholder='To' fullWidth - flush />
) @@ -1537,7 +1533,6 @@ function TagFilterValueControl({ entry, onChange }: TagFilterValueControlProps) onChange={(value) => onChange({ value })} placeholder='Select date' fullWidth - flush /> ) } @@ -1685,7 +1680,6 @@ function TagFilterSection({ tagDefinitions, entries, onChange }: TagFilterSectio matchTriggerWidth={false} contentClassName='max-h-[240px] overflow-y-auto' className='max-w-[150px]' - flush /> {entry.tagSlot && ( )}
diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/action-bar/action-bar.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/action-bar/action-bar.tsx index 052cd60622b..e6817dce63c 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/action-bar/action-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/action-bar/action-bar.tsx @@ -1,6 +1,6 @@ -import { Button, cn, Tooltip, Trash2 } from '@sim/emcn' +import { Button, cn, Tooltip, Trash } from '@sim/emcn' +import { Ban, Circle } from '@sim/emcn/icons' import { domAnimation, LazyMotion, m } from 'framer-motion' -import { Circle, CircleOff } from 'lucide-react' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' interface ActionBarProps { @@ -109,7 +109,7 @@ export function ActionBar({ disabled={isLoading} className='hover-hover:!text-[var(--text-inverse)] size-[28px] rounded-lg bg-[var(--surface-5)] p-0 text-[var(--text-secondary)] hover-hover:bg-[var(--brand-secondary)]' > - + Disable @@ -125,7 +125,7 @@ export function ActionBar({ disabled={isLoading} className='hover-hover:!text-[var(--text-inverse)] size-[28px] rounded-lg bg-[var(--surface-5)] p-0 text-[var(--text-secondary)] hover-hover:bg-[var(--brand-secondary)]' > - + Delete diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx index 661bc325188..529ac4567aa 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx @@ -20,7 +20,7 @@ import { handleKeyboardActivation, Search, } from '@sim/emcn' -import { ArrowLeft, Plus } from 'lucide-react' +import { ArrowLeft, Plus } from '@sim/emcn/icons' import { useParams } from 'next/navigation' import { consumeOAuthReturnContext } from '@/lib/credentials/client-state' import { diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-documents-modal/add-documents-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-documents-modal/add-documents-modal.tsx index cfdc62d32af..98c33ed9754 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-documents-modal/add-documents-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-documents-modal/add-documents-modal.tsx @@ -12,8 +12,8 @@ import { cn, Loader, } from '@sim/emcn' +import { RefreshCw, X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { RotateCcw, X } from 'lucide-react' import { useParams } from 'next/navigation' import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils' import { ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation' @@ -202,7 +202,7 @@ export function AddDocumentsModal({ onClick={() => handleRetryFile(index)} disabled={isUploading} > - + )} {field.description} diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx index b7e7b4b6145..6368f79313c 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx @@ -14,20 +14,20 @@ import { Loader, Tooltip, } from '@sim/emcn' -import { createLogger } from '@sim/logger' -import { format, formatDistanceToNow, isPast } from 'date-fns' import { - AlertCircle, - AlertTriangle, - CheckCircle2, ChevronDown, + CircleAlert, + CircleCheck, + CircleX, Pause, Play, RefreshCw, Settings, Trash, - XCircle, -} from 'lucide-react' + TriangleAlert, +} from '@sim/emcn/icons' +import { createLogger } from '@sim/logger' +import { format, formatDistanceToNow, isPast } from 'date-fns' import { consumeOAuthReturnContext, writeOAuthReturnContext } from '@/lib/credentials/client-state' import { getCanonicalScopesForProvider, getProviderIdFromServiceId } from '@/lib/oauth' import { getMissingRequiredScopes } from '@/lib/oauth/utils' @@ -378,7 +378,7 @@ function ConnectorCard({ )}
{connector.status === 'disabled' && ( - + )}
@@ -417,7 +417,7 @@ function ConnectorCard({ {connector.lastSyncError && ( - + {connector.lastSyncError} @@ -557,7 +557,7 @@ function ConnectorCard({
- + Connector disabled after repeated sync failures

@@ -710,9 +710,9 @@ function SyncHistory({ logs, isLoading }: SyncHistoryProps) { {isRunning ? ( ) : isError ? ( - + ) : ( - + )}

diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/edit-connector-modal/edit-connector-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/edit-connector-modal/edit-connector-modal.tsx index b916a31b232..5efe253f401 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/edit-connector-modal/edit-connector-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/edit-connector-modal/edit-connector-modal.tsx @@ -15,8 +15,8 @@ import { Skeleton, Tooltip, } from '@sim/emcn' +import { RefreshCw, SquareArrowUpRight } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { ExternalLink, RotateCcw } from 'lucide-react' import { ConnectorConfigFields } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-config-fields' import { hasWorkspaceMaxConnectorAccess } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-entitlements' import { SYNC_INTERVALS } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/consts' @@ -459,7 +459,7 @@ function DocumentsTab({ knowledgeBaseId, connectorId }: DocumentsTabProps) { rel='noopener noreferrer' className='flex size-5 flex-shrink-0 items-center justify-center rounded-md text-[var(--text-icon)] transition-colors hover-hover:bg-[var(--surface-5)] hover-hover:text-[var(--text-primary)]' > - + Open source document @@ -479,7 +479,7 @@ function DocumentsTab({ knowledgeBaseId, connectorId }: DocumentsTabProps) { > {doc.userExcluded ? ( <> - + Restore ) : ( diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx index 5364d754044..5d118422350 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx @@ -19,9 +19,9 @@ import { Loader, toast, } from '@sim/emcn' +import { X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { X } from 'lucide-react' import { useParams } from 'next/navigation' import { type FieldErrors, useForm } from 'react-hook-form' import { z } from 'zod' diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx index 5dfe44bff34..4a6b3a75a33 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx @@ -1004,7 +1004,6 @@ export function Knowledge() { onChange={(value) => setConnectorFilter(value === 'all' ? [] : [value])} align='start' fullWidth - flush />
@@ -1026,7 +1025,6 @@ export function Knowledge() { onChange={(value) => setContentFilter(value === 'all' ? [] : [value])} align='start' fullWidth - flush />
{memberOptions.length > 0 && ( @@ -1053,7 +1051,6 @@ export function Knowledge() { searchPlaceholder='Search members...' align='start' fullWidth - flush />
)} diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx index c1eb4880f7a..ac95638adc7 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx @@ -16,7 +16,7 @@ import { ModalDescription, ModalHeader, } from '@sim/emcn' -import { AlertCircle } from 'lucide-react' +import { CircleAlert } from '@sim/emcn/icons' import { createPortal } from 'react-dom' import { Preview } from '@/app/workspace/[workspaceId]/w/components/preview' import { useExecutionSnapshot } from '@/hooks/queries/logs' @@ -109,7 +109,7 @@ export function ExecutionSnapshot({ style={{ height, width }} >
- + Failed to load run snapshot: {error.message}
@@ -137,7 +137,7 @@ export function ExecutionSnapshot({ style={{ height, width }} >
- + Logged State Not Found
diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx index 6315cc4e304..01d725a9719 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/trace-view/trace-view.tsx @@ -19,7 +19,6 @@ import { Tooltip, useCopyToClipboard, } from '@sim/emcn' -import { formatDuration } from '@sim/utils/formatting' import { ArrowDown, ArrowUp, @@ -29,7 +28,8 @@ import { Clipboard, Search, X, -} from 'lucide-react' +} from '@sim/emcn/icons' +import { formatDuration } from '@sim/utils/formatting' import { createPortal } from 'react-dom' import type { TraceSpan } from '@/lib/logs/types' import { diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx index 3dd9a4a9d87..c51d2e92ee7 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-details/log-details.tsx @@ -22,9 +22,18 @@ import { Tooltip, useCopyToClipboard, } from '@sim/emcn' -import { Workflow, Wrench } from '@sim/emcn/icons' +import { + ArrowDown, + ArrowUp, + Check, + ChevronUp, + Clipboard, + Search, + Workflow, + Wrench, + X, +} from '@sim/emcn/icons' import { formatDuration } from '@sim/utils/formatting' -import { ArrowDown, ArrowUp, Check, ChevronUp, Clipboard, Search, X } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { useQueryState } from 'nuqs' import { createPortal } from 'react-dom' @@ -542,7 +551,7 @@ export function LogDetailsContent({ log, onActiveTabChange }: LogDetailsContentP Snapshot - setIsExecutionSnapshotOpen(true)}> + setIsExecutionSnapshotOpen(true)}> View Snapshot
@@ -554,7 +563,7 @@ export function LogDetailsContent({ log, onActiveTabChange }: LogDetailsContentP Troubleshoot - + Troubleshoot in Chat
diff --git a/apps/sim/app/workspace/[workspaceId]/not-found.tsx b/apps/sim/app/workspace/[workspaceId]/not-found.tsx index 43dbfbaea36..5dbfd3603a5 100644 --- a/apps/sim/app/workspace/[workspaceId]/not-found.tsx +++ b/apps/sim/app/workspace/[workspaceId]/not-found.tsx @@ -1,8 +1,7 @@ 'use client' import { Button, buttonVariants } from '@sim/emcn' -import { ArrowLeft, Home } from '@sim/emcn/icons' -import { Compass } from 'lucide-react' +import { ArrowLeft, Compass, Home } from '@sim/emcn/icons' import Link from 'next/link' import { useParams, useRouter } from 'next/navigation' import { ErrorShell } from '@/app/workspace/[workspaceId]/components' @@ -16,7 +15,7 @@ export default function WorkspaceNotFound() { } + icon={} > diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/billing/billing.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/billing/billing.tsx index 851bc668d9c..02820a289de 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/billing/billing.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/billing/billing.tsx @@ -467,14 +467,13 @@ export function Billing({ scope, organizationId, creditUsageHref }: BillingProps {explorePlansLabel} ) : ( - + {explorePlansLabel} ))} @@ -566,7 +565,6 @@ export function Billing({ scope, organizationId, creditUsageHref }: BillingProps
Payment method @@ -582,7 +580,6 @@ export function Billing({ scope, organizationId, creditUsageHref }: BillingProps {isCancelledAtPeriodEnd ? ( @@ -591,7 +588,6 @@ export function Billing({ scope, organizationId, creditUsageHref }: BillingProps ) : ( diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/password-detail/password-detail.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/password-detail/password-detail.tsx index 0c301136264..dfece1a77ab 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/password-detail/password-detail.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/browser/components/password-detail/password-detail.tsx @@ -176,7 +176,7 @@ export function PasswordDetail({ credential, onBack, onForgotten }: PasswordDeta @@ -478,7 +478,7 @@ export function General() { aria-label='About canvas error notifications' className='inline-flex cursor-default text-[var(--text-muted)]' > - + diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-settings-tab/inbox-settings-tab.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-settings-tab/inbox-settings-tab.tsx index d34e1036307..1ad98916978 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-settings-tab/inbox-settings-tab.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-settings-tab/inbox-settings-tab.tsx @@ -14,8 +14,8 @@ import { Tooltip, useCopyToClipboard, } from '@sim/emcn' +import { Check, Clipboard, Pencil, Plus, Trash } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' -import { Check, Clipboard, Pencil, Plus, Trash2 } from 'lucide-react' import { useParams } from 'next/navigation' import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' import { @@ -190,8 +190,7 @@ export function InboxSettingsTab() { )}
handleRemoveSender(sender.id)} /> diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx index df0a8c8dc1d..4fb79420131 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/inbox/components/inbox-task-list/inbox-task-list.tsx @@ -2,8 +2,8 @@ import { useCallback, useMemo } from 'react' import { Badge, ChipInput, ChipSelect, Search } from '@sim/emcn' +import { ArrowRight, Paperclip } from '@sim/emcn/icons' import { formatRelativeTime } from '@sim/utils/formatting' -import { ArrowRight, Paperclip } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { useQueryStates } from 'nuqs' import { diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx index f3afefe0820..1829b8da4d2 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx @@ -14,9 +14,9 @@ import { cn, SecretInput, } from '@sim/emcn' +import { ChevronDown, ChevronRight } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { ChevronDown, ChevronRight } from 'lucide-react' import type { McpAuthType, McpTransport } from '@/lib/mcp/types' import { checkEnvVarTrigger, diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx index 5558e23fb35..b6a9157ac72 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx @@ -2,10 +2,9 @@ import { useEffect, useRef, useState } from 'react' import { Badge, Button, Chip, ChipConfirmModal, cn, Tooltip, toast } from '@sim/emcn' -import { ArrowLeft } from '@sim/emcn/icons' +import { ArrowLeft, ChevronDown, Plus } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { ChevronDown, Plus } from 'lucide-react' import { useParams } from 'next/navigation' import { useQueryState } from 'nuqs' import { canMutateWorkspaceSettingsSection } from '@/components/settings/navigation' diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/row-actions-menu/row-actions-menu.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/row-actions-menu/row-actions-menu.tsx index f082654c8c7..5c3d29bb494 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/row-actions-menu/row-actions-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/row-actions-menu/row-actions-menu.tsx @@ -39,11 +39,7 @@ export function RowActionsMenu({ label, actions, triggerClassName }: RowActionsM return ( - diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx index 63720940891..8df81724bb1 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/team-management/components/team-seats-overview/team-seats-overview.tsx @@ -46,7 +46,7 @@ export function TeamSeatsOverview({
{billingHref && ( - + View plans )} diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/teammates/teammates.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/teammates/teammates.tsx index b9b8763bc0f..565af151a52 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/teammates/teammates.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/teammates/teammates.tsx @@ -36,7 +36,7 @@ import { useWorkspacePermissionsQuery, useWorkspacesQuery, } from '@/hooks/queries/workspace' -import { usePermissionConfig } from '@/hooks/use-permission-config' +import { useWorkspaceInvitePolicy } from '@/hooks/use-workspace-invite-policy' const ROLE_OPTIONS = [ { value: 'read', label: 'Read' }, @@ -89,7 +89,7 @@ export function Teammates() { const router = useRouter() const queryClient = useQueryClient() - const { isInvitationsDisabled: isInvitationsDisabledByConfig } = usePermissionConfig() + const { inviteDisabledReason, isInvitationsDisabled } = useWorkspaceInvitePolicy(workspaceId) const resendInvitation = useResendWorkspaceInvitation() const cancelInvitation = useCancelWorkspaceInvitation() @@ -103,8 +103,6 @@ export function Teammates() { }) const activeWorkspace = workspaces?.find((workspace) => workspace.id === workspaceId) - const inviteDisabledReason = activeWorkspace?.inviteDisabledReason ?? null - const isInvitationsDisabled = isInvitationsDisabledByConfig || inviteDisabledReason !== null const upgradeHref = buildUpgradeHref(workspaceId, 'seats') diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx index f916e798322..beffa3c822f 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/workflow-mcp-servers/workflow-mcp-servers.tsx @@ -21,10 +21,9 @@ import { Label, useCopyToClipboard, } from '@sim/emcn' -import { ArrowLeft } from '@sim/emcn/icons' +import { ArrowLeft, Check, Clipboard, Plus, Server } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { Check, Clipboard, Plus, Server } from 'lucide-react' import { useParams } from 'next/navigation' import { useQueryState } from 'nuqs' import { canMutateWorkspaceSettingsSection } from '@/components/settings/navigation' diff --git a/apps/sim/app/workspace/[workspaceId]/skills/page.tsx b/apps/sim/app/workspace/[workspaceId]/skills/page.tsx index 219042c8ce1..da0f1cb37bc 100644 --- a/apps/sim/app/workspace/[workspaceId]/skills/page.tsx +++ b/apps/sim/app/workspace/[workspaceId]/skills/page.tsx @@ -1,6 +1,6 @@ import { Suspense } from 'react' import type { Metadata } from 'next' -import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/integrations/components/integration-tabs-header' +import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/components' import { Skills } from '@/app/workspace/[workspaceId]/skills/skills' export const metadata: Metadata = { diff --git a/apps/sim/app/workspace/[workspaceId]/skills/skills.tsx b/apps/sim/app/workspace/[workspaceId]/skills/skills.tsx index 2f2dd9ad2ff..e5043d6de4f 100644 --- a/apps/sim/app/workspace/[workspaceId]/skills/skills.tsx +++ b/apps/sim/app/workspace/[workspaceId]/skills/skills.tsx @@ -2,12 +2,11 @@ import { useEffect, useRef } from 'react' import { Chip, ChipInput, Search } from '@sim/emcn' +import { ArrowRight, Plus } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' -import { ArrowRight, Plus } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { useQueryState } from 'nuqs' -import { SkillTile } from '@/app/workspace/[workspaceId]/components' -import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/integrations/components/integration-tabs-header' +import { IntegrationTabsHeader, SkillTile } from '@/app/workspace/[workspaceId]/components' import { ShowcaseWithExplore } from '@/app/workspace/[workspaceId]/integrations/components/showcase-with-explore' import { skillIdParam, diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/new-column-dropdown/new-column-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/new-column-dropdown/new-column-dropdown.tsx index 161b4a32a51..466b4af14ce 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/new-column-dropdown/new-column-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/new-column-dropdown/new-column-dropdown.tsx @@ -12,7 +12,7 @@ import { DropdownMenuTrigger, Plus, } from '@sim/emcn' -import { Sparkles } from 'lucide-react' +import { Sparkles } from '@sim/emcn/icons' import type { ColumnDefinition } from '@/lib/table' import { COLUMN_TYPE_OPTIONS } from '../column-config-sidebar' diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx index 092e7046e79..d46e2193330 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx @@ -269,7 +269,6 @@ function ColumnField({ column, value, onChange }: ColumnFieldProps) { today={todayLocalCalendarDate(timeZone)} onChange={(day) => onChange(localPartsToDateValue(day, parts.time, timeZone))} placeholder='Select date' - flush className='flex-1' />
diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-find.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-find.tsx index d8d176406a8..58b9220bdcb 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-find.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-find.tsx @@ -2,8 +2,7 @@ import type React from 'react' import { Button, ChipInput } from '@sim/emcn' -import { Loader, X } from '@sim/emcn/icons' -import { ChevronDown, ChevronUp } from 'lucide-react' +import { ChevronDown, ChevronUp, Loader, X } from '@sim/emcn/icons' export interface TableFindProps { query: string 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 d5d67dc2a83..8468c202805 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 @@ -18,11 +18,10 @@ import { Tooltip, toast, } from '@sim/emcn' -import { ArrowLeft, ChevronDown, X } from '@sim/emcn/icons' +import { ArrowLeft, ChevronDown, Repeat, Split, SquareArrowUpRight, X } from '@sim/emcn/icons' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import { useMutation, useQueryClient } from '@tanstack/react-query' -import { ExternalLink, RepeatIcon, SplitIcon } from 'lucide-react' import { findValidationIssue, isValidationError } from '@/lib/api/client/errors' import { requestJson } from '@/lib/api/client/request' import type { @@ -475,8 +474,8 @@ export function WorkflowSidebarBody({ .charAt(0) .toUpperCase() if (blockConfig?.icon) blockIcon = blockConfig.icon - else if (f.blockType === 'loop') blockIcon = RepeatIcon - else if (f.blockType === 'parallel') blockIcon = SplitIcon + else if (f.blockType === 'loop') blockIcon = Repeat + else if (f.blockType === 'parallel') blockIcon = Split group = { blockId: f.blockId, blockName: f.blockName, @@ -902,7 +901,7 @@ export function WorkflowSidebarBody({ } className='absolute right-[6px] bottom-1.5 z-10 size-[24px] cursor-pointer border border-[var(--border)] bg-[var(--surface-2)] p-0 hover-hover:bg-[var(--surface-4)]' > - + Open workflow diff --git a/apps/sim/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-table.tsx b/apps/sim/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-table.tsx index 90e060302f0..261d903b1b1 100644 --- a/apps/sim/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-table.tsx +++ b/apps/sim/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-table.tsx @@ -176,7 +176,7 @@ export function ComparisonTable({ disabled={cta.disabled} aria-label={`${cta.label} — ${col.name}`} className={cn( - chipVariants({ variant: cta.variant, fullWidth: true, flush: true }), + chipVariants({ variant: cta.variant, fullWidth: true }), 'mt-2 w-full justify-center' )} > diff --git a/apps/sim/app/workspace/[workspaceId]/upgrade/components/plan-card/plan-card.tsx b/apps/sim/app/workspace/[workspaceId]/upgrade/components/plan-card/plan-card.tsx index 808be0af4a0..fc8b96109e8 100644 --- a/apps/sim/app/workspace/[workspaceId]/upgrade/components/plan-card/plan-card.tsx +++ b/apps/sim/app/workspace/[workspaceId]/upgrade/components/plan-card/plan-card.tsx @@ -99,7 +99,6 @@ export function UpgradePlanCard({ chipVariants({ variant: highlighted ? 'primary' : 'border-shadow', fullWidth: true, - flush: true, }), 'w-full justify-center' )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx index 08bde247dbc..979d96bfcf9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx @@ -1,6 +1,6 @@ import { memo, useCallback } from 'react' -import { Button, cn, Duplicate, PlayOutline, Tooltip, Trash2, toast } from '@sim/emcn' -import { ArrowLeftRight, ArrowUpDown, Circle, CircleOff, Lock, LogOut, Unlock } from 'lucide-react' +import { Button, cn, Duplicate, PlayOutline, Tooltip, Trash, toast } from '@sim/emcn' +import { ArrowLeftRight, ArrowUpDown, Ban, Circle, Lock, LogOut, Unlock } from '@sim/emcn/icons' import { useShallow } from 'zustand/react/shallow' import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' @@ -210,7 +210,7 @@ export const ActionBar = memo( disabled || isLocked || isParentLocked || (!isEnabled && isParentDisabled) } > - {isEnabled ? : } + {isEnabled ? : } @@ -345,7 +345,7 @@ export const ActionBar = memo( className={ACTION_BUTTON_STYLES} disabled={disabled || isLocked || isParentLocked} > - + diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.test.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.test.tsx index 682e8ddc52b..9469d62fd28 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.test.tsx @@ -103,12 +103,9 @@ vi.mock('@sim/emcn', () => ({ })) vi.mock('@sim/emcn/icons', () => ({ - Download: () => , -})) - -vi.mock('lucide-react', () => ({ - AlertCircle: () => , ArrowUp: () => , + CircleAlert: () => , + Download: () => , MoreVertical: () => , Paperclip: () => , Square: () => , diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx index b002bc41896..ecaba8b177c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx @@ -14,10 +14,9 @@ import { Tooltip, Trash, } from '@sim/emcn' -import { Download } from '@sim/emcn/icons' +import { ArrowUp, CircleAlert, Download, MoreVertical, Paperclip, Square, X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { generateId } from '@sim/utils/id' -import { AlertCircle, ArrowUp, MoreVertical, Paperclip, Square, X } from 'lucide-react' import { useShallow } from 'zustand/react/shallow' import { useSession } from '@/lib/auth/auth-client' import { @@ -957,7 +956,7 @@ export function Chat() { className='!p-1.5 -m-1.5' onClick={(e) => e.stopPropagation()} > - +
- +
File upload error @@ -1122,10 +1121,7 @@ export function Chat() { : 'bg-[#808080] dark:bg-[#808080]' )} > - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/chat-message/chat-message.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/chat-message/chat-message.tsx index 7ac8d75415f..aa7f25a3ef8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/chat-message/chat-message.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/chat-message/chat-message.tsx @@ -91,7 +91,7 @@ export function ChatMessage({ message }: ChatMessageProps) { return (
-
+
{message.isStreaming && }
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx index c6c9ee01ec6..51ddef73a41 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select.tsx @@ -3,7 +3,7 @@ import type React from 'react' import { useMemo } from 'react' import { Combobox, type ComboboxOptionGroup, cn } from '@sim/emcn' -import { RepeatIcon, SplitIcon } from 'lucide-react' +import { Repeat, Split } from '@sim/emcn/icons' import { useShallow } from 'zustand/react/shallow' import { type FlattenOutputsBlockInput, @@ -264,9 +264,9 @@ export function OutputSelect({ if (blockConfig?.icon) { blockIcon = blockConfig.icon } else if (firstOutput.blockType === 'loop') { - blockIcon = RepeatIcon + blockIcon = Repeat } else if (firstOutput.blockType === 'parallel') { - blockIcon = SplitIcon + blockIcon = Split } return { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/command-list/command-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/command-list/command-list.tsx index cac027c4a73..9d79ae7bfa8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/command-list/command-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/command-list/command-list.tsx @@ -2,8 +2,8 @@ import { useCallback } from 'react' import { Button, cn, handleKeyboardActivation, Library } from '@sim/emcn' +import { Search } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { Search } from 'lucide-react' import Image from 'next/image' import { useParams, useRouter } from 'next/navigation' import { AgentIcon } from '@/components/icons' @@ -18,7 +18,7 @@ const logger = createLogger('WorkflowCommandList') interface CommandItem { /** Display label for the command */ label: string - /** Icon component from lucide-react */ + /** Icon component rendered beside the label */ icon: React.ComponentType<{ className?: string }> /** Keyboard shortcut keys (can be single or array for multiple keys) */ shortcut: string | string[] diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/error/index.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/error/index.tsx index c236ee2e5b6..b5d28d9de17 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/error/index.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/error/index.tsx @@ -2,8 +2,8 @@ import { Component, type ReactNode, useEffect } from 'react' import { Button } from '@sim/emcn' +import { RefreshCw } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { RefreshCw } from 'lucide-react' import { ReactFlowProvider } from 'reactflow' import { Panel } from '@/app/workspace/[workspaceId]/w/[workflowId]/components' import { usePreventZoom } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx index c468acb2a3d..04b6ce60157 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/api/api.tsx @@ -11,7 +11,7 @@ import { Skeleton, Tooltip, } from '@sim/emcn' -import { Check, Clipboard } from 'lucide-react' +import { Check, Clipboard } from '@sim/emcn/icons' import { AGENT_STREAM_PROTOCOL_HEADER_LABEL, AGENT_STREAM_PROTOCOL_V1, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx index f08467fe3e6..e0d873ecc2e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx @@ -17,10 +17,10 @@ import { Textarea, Tooltip, } from '@sim/emcn' +import { Check, TriangleAlert } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' import { normalizeEmail } from '@sim/utils/string' -import { AlertTriangle, Check } from 'lucide-react' import { GeneratedPasswordInput } from '@/components/ui' import { isSsoEnabled } from '@/lib/core/config/env-flags' import { getBaseUrl, getEmailDomain } from '@/lib/core/utils/urls' @@ -320,7 +320,7 @@ export function ChatDeploy({ > {errors.general && (
- + {errors.general}
)} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/versions.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/versions.tsx index 868bca830f8..305d3c708fc 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/versions.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/components/versions.tsx @@ -12,8 +12,8 @@ import { Skeleton, Tooltip, } from '@sim/emcn' +import { FileText, MoreVertical, Pencil, RefreshCw, SendToBack } from '@sim/emcn/icons' import { formatDateTime } from '@sim/utils/formatting' -import { FileText, MoreVertical, Pencil, RotateCcw, SendToBack } from 'lucide-react' import type { WorkflowDeploymentVersionResponse } from '@/lib/workflows/persistence/utils' import { formatVersionLabel } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/format-version-label' import { useUpdateDeploymentVersion } from '@/hooks/queries/deployments' @@ -374,7 +374,7 @@ export function Versions({ {!v.isActive && ( handlePromote(v.version)}> - + Promote to live )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/components/field-item/field-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/components/field-item/field-item.tsx index 916c284eb9b..a88b1726e19 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/components/field-item/field-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/components/field-item/field-item.tsx @@ -2,9 +2,9 @@ import { useCallback } from 'react' import { Badge, handleKeyboardActivation } from '@sim/emcn' +import { ChevronDown } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import clsx from 'clsx' -import { ChevronDown } from 'lucide-react' import type { ConnectedBlock } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/hooks/use-block-connections' import { normalizeName } from '@/executor/constants' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx index d28620e2a38..0e8688c8899 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/connection-blocks/connection-blocks.tsx @@ -2,9 +2,9 @@ import { useCallback, useRef, useState } from 'react' import { ChevronDown, handleKeyboardActivation } from '@sim/emcn' +import { Repeat, Split } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import clsx from 'clsx' -import { RepeatIcon, SplitIcon } from 'lucide-react' import { useShallow } from 'zustand/react/shallow' import { FieldItem, @@ -118,10 +118,10 @@ function ConnectionItem({ if (!blockConfig) { if (connection.type === 'loop') { - Icon = RepeatIcon as typeof Icon + Icon = Repeat bgColor = '#2FB3FF' } else if (connection.type === 'parallel') { - Icon = SplitIcon as typeof Icon + Icon = Split bgColor = '#FEE12B' } } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/checkbox-list/checkbox-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/checkbox-list/checkbox-list.tsx index 992aeeb0164..403827b6660 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/checkbox-list/checkbox-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/checkbox-list/checkbox-list.tsx @@ -1,5 +1,5 @@ import { Checkbox, Label, Tooltip } from '@sim/emcn' -import { Info } from 'lucide-react' +import { CircleInfo } from '@sim/emcn/icons' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' @@ -85,7 +85,7 @@ function CheckboxItem({ {option.description && ( - +

{option.description}

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 a0d18f4d63b..8cfef78abef 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 @@ -10,8 +10,8 @@ import { highlight, languages, } from '@sim/emcn' +import { Check, Wand } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { Check, Wand2 } from 'lucide-react' import { useParams } from 'next/navigation' import Editor from 'react-simple-code-editor' import { Button } from '@/components/ui/button' @@ -865,7 +865,7 @@ export const Code = memo(function Code({ aria-label='Generate code with AI' className='size-8 rounded-full border border-transparent bg-muted/80 text-muted-foreground shadow-sm transition-all duration-200 hover-hover:border-primary/20 hover-hover:bg-muted hover-hover:text-foreground hover-hover:shadow' > - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/condition-input/condition-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/condition-input/condition-input.tsx index 1099461437e..c0bcab52da0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/condition-input/condition-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/condition-input/condition-input.tsx @@ -11,9 +11,8 @@ import { Textarea, Tooltip, } from '@sim/emcn' -import { Trash } from '@sim/emcn/icons' +import { ChevronDown, ChevronsUpDown, ChevronUp, Plus, Trash } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { ChevronDown, ChevronsUpDown, ChevronUp, Plus } from 'lucide-react' import { useParams } from 'next/navigation' import Editor from 'react-simple-code-editor' import { isElseConditionTitle } from '@/lib/workflows/conditions' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx index 466224ba63f..fabdcb43c7a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/credential-selector.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo, useState } from 'react' import { Button, Combobox, type ComboboxOptionGroup } from '@sim/emcn' -import { ExternalLink, KeyRound } from 'lucide-react' +import { Key, SquareArrowUpRight } from '@sim/emcn/icons' import { useParams } from 'next/navigation' import { consumeOAuthReturnContext, writeOAuthReturnContext } from '@/lib/credentials/client-state' import { @@ -231,7 +231,7 @@ export function CredentialSelector({ const baseProviderConfig = OAUTH_PROVIDERS[baseProvider] if (!baseProviderConfig) { - return + return } const Icon: StyleableIcon = baseProviderConfig.icon return @@ -278,7 +278,7 @@ export function CredentialSelector({ ? `Connect another ${getProviderName(provider)} account` : `Connect ${getProviderName(provider)} account`, value: '__connect_account__', - iconElement: , + iconElement: , }) } @@ -315,7 +315,7 @@ export function CredentialSelector({ { label: labels?.oauthConnect ?? `Connect ${getProviderName(provider)} account`, value: '__connect_account__', - iconElement: , + iconElement: , }, ], }, @@ -333,7 +333,7 @@ export function CredentialSelector({ serviceAccountTarget?.label ?? `Add ${getProviderName(provider)} key`, value: '__connect_service_account__', - iconElement: , + iconElement: , }, ]), ], @@ -365,7 +365,7 @@ export function CredentialSelector({ return (
- +
{formatDisplayText(displayValue, { workflowSearchHighlight })} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx index 4e51a715a66..524e7461d47 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/document-tag-entry/document-tag-entry.tsx @@ -12,8 +12,8 @@ import { Label, Trash, } from '@sim/emcn' +import { Plus } from '@sim/emcn/icons' import { generateId } from '@sim/utils/id' -import { Plus } from 'lucide-react' import { FIELD_TYPE_LABELS, getPlaceholderForFieldType } from '@/lib/knowledge/constants' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx index dfa0412e510..b20ba4efc87 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown/env-var-dropdown.tsx @@ -8,7 +8,7 @@ import { PopoverScrollArea, PopoverSection, } from '@sim/emcn' -import { Plus } from 'lucide-react' +import { Plus } from '@sim/emcn/icons' import { writePendingCredentialCreateRequest } from '@/lib/credentials/client-state' import type { WorkspaceEnvironmentData } from '@/lib/environment/api' import { usePersonalEnvironment, useWorkspaceEnvironment } from '@/hooks/queries/environment' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx index 621e07b38e9..fe7ee8c53af 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/eval-input/eval-input.tsx @@ -1,8 +1,7 @@ import { useMemo, useRef } from 'react' import { Button, cn, Input, Label, Textarea, Tooltip } from '@sim/emcn' -import { Trash } from '@sim/emcn/icons' +import { Plus, Trash } from '@sim/emcn/icons' import { generateId } from '@sim/utils/id' -import { Plus } from 'lucide-react' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown' import { getActiveWorkflowSearchHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx index 4b0368813d7..2b7356c6697 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/file-upload/file-upload.tsx @@ -2,12 +2,12 @@ import { useMemo, useRef, useState } from 'react' import { Button, Combobox, cn } from '@sim/emcn' +import { X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' import { generateShortId } from '@sim/utils/id' import { randomFloat } from '@sim/utils/random' import { useQueryClient } from '@tanstack/react-query' -import { X } from 'lucide-react' import { useParams } from 'next/navigation' import { Progress } from '@/components/ui/progress' import { isApiClientError } from '@/lib/api/client/errors' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx index 9cf97b73144..b0f33f1dded 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/components/filter-rule-row.tsx @@ -10,7 +10,7 @@ import { Label, Trash, } from '@sim/emcn' -import { Plus } from 'lucide-react' +import { Plus } from '@sim/emcn/icons' import type { FilterRule } from '@/lib/table/query-builder/constants' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx index 4075a2f72de..4df315eb4c4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/filter-builder/filter-builder.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react' import { Button } from '@sim/emcn' -import { Plus } from 'lucide-react' +import { Plus } from '@sim/emcn/icons' import { useTableColumns } from '@/lib/table/hooks' import type { FilterRule } from '@/lib/table/query-builder/constants' import { useFilterBuilder } from '@/lib/table/query-builder/use-query-builder' 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 ac2673216a3..5d4b10ea038 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 @@ -10,7 +10,7 @@ import { ChipModalHeader, cn, } from '@sim/emcn' -import { Settings2 } from 'lucide-react' +import { ManageWorkspace } from '@sim/emcn/icons' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' @@ -135,7 +135,7 @@ export function GroupedCheckboxList({ )} > - + Configure PII Types - +
)} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/messages-input/messages-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/messages-input/messages-input.tsx index 6f1367e2141..a010caf0eef 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/messages-input/messages-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/messages-input/messages-input.tsx @@ -8,10 +8,9 @@ import { useState, } from 'react' import { Button, cn, Popover, PopoverContent, PopoverItem, PopoverTrigger } from '@sim/emcn' -import { Trash } from '@sim/emcn/icons' +import { ChevronDown, ChevronsUpDown, ChevronUp, Plus, Trash } from '@sim/emcn/icons' import { generateShortId } from '@sim/utils/id' import { isEqual } from 'es-toolkit' -import { ChevronDown, ChevronsUpDown, ChevronUp, Plus } from 'lucide-react' import { EnvVarDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx index 6768c2e0e87..920177208e4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox.tsx @@ -1,7 +1,7 @@ import type React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Button, Combobox as EditableCombobox } from '@sim/emcn' -import { X } from 'lucide-react' +import { X } from '@sim/emcn/icons' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { SubBlockInputController } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sub-block-input-controller' import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input.tsx index d6befd56167..fd9181674e8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/short-input/short-input.tsx @@ -1,6 +1,6 @@ import { memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react' import { cn, Input } from '@sim/emcn' -import { Wand2 } from 'lucide-react' +import { Wand } from '@sim/emcn/icons' import { useReactFlow } from 'reactflow' import { Button } from '@/components/ui/button' import { @@ -418,7 +418,7 @@ export const ShortInput = memo(function ShortInput({ aria-label='Generate content with AI' className='size-8 rounded-full border border-transparent bg-muted/80 text-muted-foreground shadow-sm transition-all duration-200 hover-hover:border-primary/20 hover-hover:bg-muted hover-hover:text-foreground hover-hover:shadow' > - +
)} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/skill-input/skill-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/skill-input/skill-input.tsx index ac2282c5b62..c7f83381d6e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/skill-input/skill-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/skill-input/skill-input.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo, useState } from 'react' import { Combobox, type ComboboxOptionGroup } from '@sim/emcn' -import { Plus, XIcon } from 'lucide-react' +import { Plus, X } from '@sim/emcn/icons' import { useParams } from 'next/navigation' import { AgentSkillsIcon } from '@/components/icons' import { SkillModal } from '@/app/workspace/[workspaceId]/skills/components/skill-modal' @@ -183,7 +183,7 @@ export function SkillInput({ className='flex items-center justify-center text-[var(--text-tertiary)] transition-colors hover-hover:text-[var(--text-primary)]' aria-label='Remove skill' > - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-setup-wizard/slack-setup-wizard.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-setup-wizard/slack-setup-wizard.tsx index 3d3745abb4c..a201310d374 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-setup-wizard/slack-setup-wizard.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/slack-setup-wizard/slack-setup-wizard.tsx @@ -2,7 +2,7 @@ import { type ReactNode, useCallback, useMemo, useState } from 'react' import { Checkbox, cn, Input, Label, SecretInput, Tooltip, Wizard } from '@sim/emcn' -import { Check, ChevronRight, Clipboard, Info } from 'lucide-react' +import { Check, ChevronRight, CircleInfo, Clipboard } from '@sim/emcn/icons' import { useShallow } from 'zustand/react/shallow' import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value' import { useWebhookManagement } from '@/hooks/use-webhook-management' @@ -541,7 +541,7 @@ function CapabilityRow({ blockId, capability, checked, disabled }: CapabilityRow - +

{capability.description}

diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/components/sort-rule-row.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/components/sort-rule-row.tsx index 35c68663cde..94d320d805b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/components/sort-rule-row.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/components/sort-rule-row.tsx @@ -8,7 +8,7 @@ import { Label, Trash, } from '@sim/emcn' -import { Plus } from 'lucide-react' +import { Plus } from '@sim/emcn/icons' import type { SortRule } from '@/lib/table/query-builder/constants' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { getWorkflowSearchLabelHighlight } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/workflow-search-highlight' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx index 9427c7a8ca5..c9963668a00 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/sort-builder/sort-builder.tsx @@ -2,8 +2,8 @@ import { useCallback, useMemo } from 'react' import { Button, type ComboboxOption } from '@sim/emcn' +import { Plus } from '@sim/emcn/icons' import { generateId } from '@sim/utils/id' -import { Plus } from 'lucide-react' import { useTableColumns } from '@/lib/table/hooks' import { SORT_DIRECTION_OPTIONS, type SortRule } from '@/lib/table/query-builder/constants' import { useCanonicalSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-canonical-sub-block-value' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx index 25324d61f91..b26fd8ddc3b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/starter/input-format.tsx @@ -17,8 +17,7 @@ import { languages, Tooltip, } from '@sim/emcn' -import { Trash } from '@sim/emcn/icons' -import { ArrowLeftRight, Plus } from 'lucide-react' +import { ArrowLeftRight, Plus, Trash } from '@sim/emcn/icons' import Editor from 'react-simple-code-editor' import { createDefaultInputFormatField, diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx index cd8b9df42cf..69fb1b5a74e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx @@ -11,8 +11,8 @@ import { PopoverSection, usePopoverContext, } from '@sim/emcn' +import { Repeat, Split } from '@sim/emcn/icons' import { isEqual } from 'es-toolkit' -import { RepeatIcon, SplitIcon } from 'lucide-react' import { useShallow } from 'zustand/react/shallow' import { useStoreWithEqualityFn } from 'zustand/traditional' import { @@ -1767,9 +1767,9 @@ export const TagDropdown: React.FC = ({ if (blockConfig?.icon) { tagIcon = blockConfig.icon } else if (group.blockType === 'loop') { - tagIcon = RepeatIcon + tagIcon = Repeat } else if (group.blockType === 'parallel') { - tagIcon = SplitIcon + tagIcon = Split } const normalizedBlockName = normalizeName(group.blockName) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx index d11ab696cad..624f8bf34d6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/credential-selector.tsx @@ -2,7 +2,7 @@ import { createElement, useCallback, useMemo, useRef, useState } from 'react' import { Button, Combobox } from '@sim/emcn' -import { ExternalLink } from 'lucide-react' +import { SquareArrowUpRight } from '@sim/emcn/icons' import { useParams } from 'next/navigation' import { consumeOAuthReturnContext, writeOAuthReturnContext } from '@/lib/credentials/client-state' import { @@ -30,7 +30,7 @@ const getProviderIcon = (providerName: OAuthProvider) => { const baseProviderConfig = OAUTH_PROVIDERS[baseProvider] if (!baseProviderConfig) { - return + return } return createElement(baseProviderConfig.icon, { className: 'size-3' }) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/parameter.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/parameter.tsx index f756c6d127e..7bd4d1d339c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/parameter.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/tools/parameter.tsx @@ -3,7 +3,7 @@ import type React from 'react' import { useRef, useState } from 'react' import { Button, cn, Input, Label, Tooltip } from '@sim/emcn' -import { ArrowLeftRight, ArrowUp } from 'lucide-react' +import { ArrowLeftRight, ArrowUp } from '@sim/emcn/icons' import type { WandControlHandlers } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block' /** 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 0b34f353382..f31d80571fd 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 @@ -14,8 +14,8 @@ import { Switch, Tooltip, } from '@sim/emcn' +import { ArrowLeft, ChevronRight, Server, Wrench, X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { ArrowLeft, ChevronRight, ServerIcon, WrenchIcon, XIcon } from 'lucide-react' import { useParams } from 'next/navigation' import { McpIcon, WorkflowIcon } from '@/components/icons' import { @@ -1415,7 +1415,7 @@ export const ToolInput = memo(function ToolInput({ serverToolItems.push({ label: `Use all ${toolCount} tools`, value: `mcp-server-all-${mcpServerDrilldown}`, - iconElement: createToolIcon('#6366F1', ServerIcon), + iconElement: createToolIcon('#6366F1', Server), onSelect: () => { if (allAlreadySelected) return // Remove existing individual tools from this server to avoid duplicates @@ -1497,7 +1497,7 @@ export const ToolInput = memo(function ToolInput({ actionItems.push({ label: 'Create Tool', value: 'action-create-tool', - icon: WrenchIcon, + icon: Wrench, onSelect: () => { setCustomToolModalOpen(true) setOpen(false) @@ -1535,7 +1535,7 @@ export const ToolInput = memo(function ToolInput({ return { label: customTool.title, value: `custom-${customTool.id}`, - iconElement: createToolIcon('#3B82F6', WrenchIcon), + iconElement: createToolIcon('#3B82F6', Wrench), disabled: isPreview || alreadySelected, onSelect: () => { if (alreadySelected) return @@ -1568,7 +1568,7 @@ export const ToolInput = memo(function ToolInput({ serverItems.push({ label: `${serverName} (${toolCount} tools)`, value: `mcp-server-folder-${serverId}`, - iconElement: createToolIcon('#6366F1', ServerIcon), + iconElement: createToolIcon('#6366F1', Server), suffixElement: , onSelect: () => { setMcpServerDrilldown(serverId) @@ -1883,7 +1883,7 @@ export const ToolInput = memo(function ToolInput({ }} > {isCustomTool ? ( - + ) : isMcpTool ? ( - + - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx index 4c2e668c459..7c83e20d8d9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/variables-input/variables-input.tsx @@ -11,9 +11,8 @@ import { Textarea, Tooltip, } from '@sim/emcn' -import { Trash } from '@sim/emcn/icons' +import { ArrowLeftRight, Plus, Trash } from '@sim/emcn/icons' import { generateId } from '@sim/utils/id' -import { ArrowLeftRight, Plus } from 'lucide-react' import { useParams } from 'next/navigation' import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text' import { diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx index 8832dd0ef54..d9fd577e04f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx @@ -1,14 +1,14 @@ import { type JSX, type MouseEvent, memo, useCallback, useMemo, useRef, useState } from 'react' import { Button, cn, Input, Label, Tooltip } from '@sim/emcn' -import { isEqual } from 'es-toolkit' import { - AlertTriangle, ArrowLeftRight, ArrowUp, Check, Clipboard, - ExternalLink, -} from 'lucide-react' + SquareArrowUpRight, + TriangleAlert, +} from '@sim/emcn/icons' +import { isEqual } from 'es-toolkit' import { useParams } from 'next/navigation' import type { FilterRule, SortRule } from '@/lib/table/query-builder/constants' import { @@ -263,7 +263,7 @@ const renderLabel = ( - + @@ -364,7 +364,7 @@ const renderLabel = ( onClick={externalLink?.onClick} aria-label={externalLink?.tooltip} > - + diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/subflow-editor/subflow-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/subflow-editor/subflow-editor.tsx index d7f22cb3354..318d5d30052 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/subflow-editor/subflow-editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/subflow-editor/subflow-editor.tsx @@ -8,7 +8,7 @@ import { Input, Label, } from '@sim/emcn' -import { ChevronUp } from 'lucide-react' +import { ChevronUp } from '@sim/emcn/icons' import SimpleCodeEditor from 'react-simple-code-editor' import { WORKFLOW_SEARCH_SUBFLOW_FIELD_IDS } from '@/lib/workflows/search-replace/subflow-fields' import { 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 95a26a33e95..79a09e5d348 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 @@ -2,17 +2,17 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Button, cn, DashedDividerLine, FieldDivider, Loader, Tooltip } from '@sim/emcn' -import { isEqual } from 'es-toolkit' import { BookOpen, Check, ChevronDown, ChevronUp, - ExternalLink, Lock, Pencil, + SquareArrowUpRight, Unlock, -} from 'lucide-react' +} from '@sim/emcn/icons' +import { isEqual } from 'es-toolkit' import { useParams } from 'next/navigation' import { usePostHog } from 'posthog-js/react' import { useShallow } from 'zustand/react/shallow' @@ -568,7 +568,7 @@ export function Editor() { onClick={handleOpenChildWorkflow} className='absolute right-[6px] bottom-1.5 z-10 size-[24px] cursor-pointer border border-[var(--border)] bg-[var(--surface-2)] p-0 hover-hover:bg-[var(--surface-4)]' > - + Open workflow diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/toolbar/toolbar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/toolbar/toolbar.tsx index 7abb72d398c..ff6b7e76f75 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/toolbar/toolbar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/toolbar/toolbar.tsx @@ -20,8 +20,8 @@ import { handleKeyboardActivation, Info, } from '@sim/emcn' +import { ChevronDown, Search } from '@sim/emcn/icons' import clsx from 'clsx' -import { ChevronDown, Search } from 'lucide-react' import { useParams } from 'next/navigation' import { usePostHog } from 'posthog-js/react' import { captureEvent } from '@/lib/posthog/client' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 1a997731fd8..c5ff55dbee7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -23,11 +23,10 @@ import { Trash, toast, } from '@sim/emcn' -import { Download, Lock, Unlock } from '@sim/emcn/icons' +import { BubbleChatDelay, Download, Lock, Plus, Square, Unlock } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { useQueryClient } from '@tanstack/react-query' -import { History, Plus, Square } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { usePostHog } from 'posthog-js/react' import { useShallow } from 'zustand/react/shallow' @@ -831,7 +830,7 @@ export const Panel = memo(function Panel() { > diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/search-replace/workflow-search-replace.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/search-replace/workflow-search-replace.tsx index 650170cae4a..73fe7d0d980 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/search-replace/workflow-search-replace.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/search-replace/workflow-search-replace.tsx @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Button, cn, Input, toast } from '@sim/emcn' -import { ChevronDown, ChevronRight, ChevronUp, X } from 'lucide-react' +import { ChevronDown, ChevronRight, ChevronUp, X } from '@sim/emcn/icons' import { useParams } from 'next/navigation' import { useShallow } from 'zustand/react/shallow' import { getWorkflowSearchDependentClears } from '@/lib/workflows/search-replace/dependencies' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts index e63a995c4e8..5eb91b7cd55 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts @@ -1,4 +1,4 @@ -import { RepeatIcon } from 'lucide-react' +import { Repeat } from '@sim/emcn/icons' /** * Loop tool configuration for the toolbar. @@ -7,7 +7,7 @@ import { RepeatIcon } from 'lucide-react' export const LoopTool = { type: 'loop', name: 'Loop', - icon: RepeatIcon, + icon: Repeat, bgColor: '#2FB3FF', docsLink: 'https://docs.sim.ai/workflows/blocks/loop', } as const diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts index 5b70ada1efe..71600a4b024 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts @@ -1,4 +1,4 @@ -import { SplitIcon } from 'lucide-react' +import { Split } from '@sim/emcn/icons' /** * Parallel tool configuration for the toolbar. @@ -7,7 +7,7 @@ import { SplitIcon } from 'lucide-react' export const ParallelTool = { type: 'parallel', name: 'Parallel', - icon: SplitIcon, + icon: Split, bgColor: '#FEE12B', docsLink: 'https://docs.sim.ai/workflows/blocks/parallel', } as const diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/filter-popover/filter-popover.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/filter-popover/filter-popover.tsx index 06bb376eac6..3ba40426fd0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/filter-popover/filter-popover.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/filter-popover/filter-popover.tsx @@ -11,8 +11,8 @@ import { PopoverSection, PopoverTrigger, } from '@sim/emcn' +import { ListFilter } from '@sim/emcn/icons' import clsx from 'clsx' -import { Filter } from 'lucide-react' import type { BlockInfo, TerminalFilters, @@ -53,7 +53,9 @@ export const FilterPopover = memo(function FilterPopover({ onClick={(e) => e.stopPropagation()} aria-label='Filters' > - + - + diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/toggle-button/toggle-button.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/toggle-button/toggle-button.tsx index edf4e0e8401..c23902878da 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/toggle-button/toggle-button.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/toggle-button/toggle-button.tsx @@ -3,8 +3,8 @@ import type React from 'react' import { memo } from 'react' import { Button } from '@sim/emcn' +import { ChevronDown } from '@sim/emcn/icons' import clsx from 'clsx' -import { ChevronDown } from 'lucide-react' export interface ToggleButtonProps { isExpanded: boolean diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx index 6d945a8bcc2..bdba4bacde1 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx @@ -12,11 +12,19 @@ import { PopoverTrigger, Tooltip, } from '@sim/emcn' -import { Download } from '@sim/emcn/icons' +import { + ArrowDown, + ArrowUp, + Database, + Download, + MoreHorizontal, + Palette, + Pause, + Trash, +} from '@sim/emcn/icons' import { formatDuration } from '@sim/utils/formatting' import { useVirtualizer } from '@tanstack/react-virtual' import clsx from 'clsx' -import { ArrowDown, ArrowUp, Database, MoreHorizontal, Palette, Pause, Trash2 } from 'lucide-react' import Link from 'next/link' import { getEnv, isTruthy } from '@/lib/core/config/env' import { sendMothershipMessage } from '@/lib/mothership/events' @@ -1434,7 +1442,7 @@ export const Terminal = memo(function Terminal() { aria-label='Clear console' className='!p-1.5 -m-1.5' > - + diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils.ts index f7f5400fbfd..bd7ae34a07e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/utils.ts @@ -1,12 +1,5 @@ import type React from 'react' -import { - AlertTriangleIcon, - BanIcon, - NetworkIcon, - RepeatIcon, - SplitIcon, - XCircleIcon, -} from 'lucide-react' +import { Ban, CircleX, Repeat, Split, TriangleAlert, Workflow } from '@sim/emcn/icons' import { getBlock } from '@/blocks' import { isWorkflowBlockType } from '@/executor/constants' import { TERMINAL_BLOCK_COLUMN_WIDTH } from '@/stores/constants' @@ -43,27 +36,27 @@ export function getBlockIcon( } if (blockType === 'loop') { - return RepeatIcon + return Repeat } if (blockType === 'parallel') { - return SplitIcon + return Split } if (blockType === 'workflow') { - return NetworkIcon + return Workflow } if (blockType === 'error') { - return XCircleIcon + return CircleX } if (blockType === 'validation') { - return AlertTriangleIcon + return TriangleAlert } if (blockType === 'cancelled') { - return BanIcon + return Ban } return null diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/variables/variables.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/variables/variables.tsx index eff41e09f82..d6676387379 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/variables/variables.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/variables/variables.tsx @@ -15,8 +15,7 @@ import { Label, languages, } from '@sim/emcn' -import { Trash } from '@sim/emcn/icons' -import { Plus, X } from 'lucide-react' +import { Plus, Trash, X } from '@sim/emcn/icons' import Editor from 'react-simple-code-editor' import { useShallow } from 'zustand/react/shallow' import { validateName } from '@/lib/core/utils/validation' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar.tsx index 89848f74c12..e0ddc69b313 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/wand-prompt-bar/wand-prompt-bar.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { cn } from '@sim/emcn' -import { SendIcon, XIcon } from 'lucide-react' +import { Send, X } from '@sim/emcn/icons' import { Button } from '@/components/ui/button' interface WandPromptBarProps { @@ -120,7 +120,7 @@ export function WandPromptBar({ onClick={handleCancel} className='size-8 rounded-full text-muted-foreground hover-hover:bg-accent/50 hover-hover:text-foreground' > - + {!isStreaming && ( @@ -131,7 +131,7 @@ export function WandPromptBar({ className='size-8 rounded-full text-muted-foreground hover-hover:bg-primary/10 hover-hover:text-foreground' disabled={isLoading || isStreaming || !promptValue.trim()} > - + )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-controls/workflow-controls.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-controls/workflow-controls.tsx index 00f78994d6c..0b41a7caf2b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-controls/workflow-controls.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-controls/workflow-controls.tsx @@ -15,8 +15,8 @@ import { Tooltip, Undo, } from '@sim/emcn' +import { SelectAll } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { Scan } from 'lucide-react' import { useReactFlow } from 'reactflow' import { useShallow } from 'zustand/react/shallow' import { useSession } from '@/lib/auth/auth-client' @@ -185,7 +185,7 @@ export const WorkflowControls = memo(function WorkflowControls() { className='size-[28px] rounded-md p-0 hover-hover:bg-[var(--surface-5)]' onClick={handleFitToView} > - + 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 6bbeafd5977..130096badc6 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 @@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Badge, Button, - ChevronDown, Code, Combobox, cn, @@ -14,21 +13,21 @@ import { Label, Tooltip, } from '@sim/emcn' -import { formatDuration } from '@sim/utils/formatting' import { ArrowDown, ArrowUp, Check, - ChevronDown as ChevronDownIcon, + ChevronDown, ChevronUp, Clipboard, - ExternalLink, - Maximize2, - RepeatIcon, + Expand, + Repeat, Search, - SplitIcon, + Split, + SquareArrowUpRight, X, -} from 'lucide-react' +} from '@sim/emcn/icons' +import { formatDuration } from '@sim/utils/formatting' import { useParams } from 'next/navigation' import { ReactFlowProvider } from 'reactflow' import { extractReferencePrefixes } from '@/lib/workflows/sanitization/references' @@ -404,7 +403,7 @@ function ConnectionsSection({ {connection.blockName}
{hasFields && ( - Variables
- Secrets
- {isExecutionMode && onDrillDown ? ( - + ) : ( - + )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-workflow/components/subflow/subflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-workflow/components/subflow/subflow.tsx index 922dc37dad9..fe45e4280f0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-workflow/components/subflow/subflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/preview/components/preview-workflow/components/subflow/subflow.tsx @@ -2,8 +2,8 @@ import { memo } from 'react' import { Badge, cn } from '@sim/emcn' +import { Repeat, Split } from '@sim/emcn/icons' import { HANDLE_POSITIONS } from '@sim/workflow-renderer' -import { RepeatIcon, SplitIcon } from 'lucide-react' import { Handle, type NodeProps, Position } from 'reactflow' import { getTileIconColorClass } from '@/blocks/icon-color' @@ -42,7 +42,7 @@ function WorkflowPreviewSubflowInner({ data }: NodeProps ariaLabel?: string children: React.ReactNode - className?: string primaryAction?: { label: string onSelect: () => void @@ -164,7 +162,7 @@ interface CollapsedWorkflowFlyoutItemProps { const EDIT_ROW_CLASS = cn( chipVariants({ active: true, fullWidth: true }), - 'mx-0 min-w-0 cursor-default select-none text-small' + 'min-w-0 cursor-default select-none text-small' ) export function CollapsedSidebarMenu({ @@ -172,11 +170,10 @@ export function CollapsedSidebarMenu({ hover, ariaLabel, children, - className, primaryAction, }: CollapsedSidebarMenuProps) { return ( -
+
{ diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/file-list/file-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/file-list/file-list.tsx index bb162d2c44d..c93a3e6f721 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/file-list/file-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/file-list/file-list.tsx @@ -2,7 +2,7 @@ import { memo, useMemo, useState } from 'react' import { cn } from '@sim/emcn' -import { ChevronRight } from 'lucide-react' +import { ChevronRight } from '@sim/emcn/icons' import Link from 'next/link' import type { WorkspaceFileRecord } from '@/lib/uploads/contexts/workspace' import type { WorkspaceFileFolderApi } from '@/hooks/queries/workspace-file-folders' @@ -84,7 +84,7 @@ const FileFolderNodeItem = memo(function FileFolderNodeItem({ )} - + {node.name} @@ -128,9 +128,7 @@ const FileFolderNodeItem = memo(function FileFolderNodeItem({ - - {file.name} - + {file.name} ) })} @@ -203,9 +201,7 @@ export const FileList = memo(function FileList({ - - {file.name} - + {file.name} ) })} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/help-modal/help-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/help-modal/help-modal.tsx index 41fe122cd38..6e734ec5c31 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/help-modal/help-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/help-modal/help-modal.tsx @@ -9,10 +9,10 @@ import { ChipModalFooter, ChipModalHeader, } from '@sim/emcn' +import { X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { useMutation } from '@tanstack/react-query' import imageCompression from 'browser-image-compression' -import { X } from 'lucide-react' import Image from 'next/image' import { Controller, useForm } from 'react-hook-form' import { z } from 'zod' @@ -250,8 +250,8 @@ export function HelpModal({ open, onOpenChange, workflowId, workspaceId }: HelpM } return ( - - onOpenChange(false)}>Help & support + + onOpenChange(false)}>Contact support
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/index.ts new file mode 100644 index 00000000000..c181faf748e --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/index.ts @@ -0,0 +1 @@ +export { SidebarFooter } from './sidebar-footer' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx new file mode 100644 index 00000000000..ff0adf795e6 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-footer/sidebar-footer.tsx @@ -0,0 +1,259 @@ +'use client' + +import type { ComponentType } from 'react' +import { + Chip, + chipContentLabelClass, + chipVariants, + cn, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + Skeleton, +} from '@sim/emcn' +import { BookOpen, Credit, HelpCircle, Settings, Trash, Users } from '@sim/emcn/icons' +import { SlackIcon } from '@/components/icons' +import { useSession } from '@/lib/auth/auth-client' +import { canViewWorkspaceBillingSettings } from '@/lib/billing/workspace-permissions' +import { isBillingEnabled } from '@/lib/core/config/env-flags' +import { getUserColor } from '@/lib/workspaces/colors' +import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider' +import type { SettingsSection } from '@/app/workspace/[workspaceId]/settings/navigation' +import { SIDEBAR_ITEM_GAP_CLASS } from '@/app/workspace/[workspaceId]/w/components/sidebar/constants' +import { SidebarTooltip } from '@/app/workspace/[workspaceId]/w/components/sidebar/sidebar' +import { useUserProfile } from '@/hooks/queries/user-profile' +import { useWorkspaceInvitePolicy } from '@/hooks/use-workspace-invite-policy' + +/** + * Settings destinations reachable from the profile menu, in display order. Labels + * and icons mirror the settings navigation entries they open, so the menu and the + * settings sidebar never disagree about what a section is called. + * + * Which of them a given viewer actually gets is decided in {@link SidebarFooter} — + * the same gates the settings sidebar and the section route apply, so the menu + * never lists a page the server would refuse. + */ +const PROFILE_MENU_ITEMS: readonly { + section: SettingsSection + label: string + icon: ComponentType<{ className?: string }> +}[] = [ + { section: 'general', label: 'Settings', icon: Settings }, + { section: 'billing', label: 'Subscription', icon: Credit }, + { section: 'teammates', label: 'Teammates', icon: Users }, + { section: 'recently-deleted', label: 'Recently deleted', icon: Trash }, +] + +interface SidebarFooterProps { + workspaceId: string + isCollapsed: boolean + showCollapsedTooltips: boolean + onOpenSettings: (section: SettingsSection) => void + onOpenDocs: () => void + onJoinSlack: () => void + onContactSupport: () => void +} + +/** + * Pinned bottom bar of the workspace sidebar: the viewer's avatar and name, which + * open a menu of their settings destinations, plus a help menu. + * + * Expanded, the two share one row — the profile claims the free width so the help + * button lands hard right, mirroring the collapse control in the workspace header. + * Collapsed, the rail is too narrow for a row, so they stack as icon chips with + * help on top and the profile resting at the foot of the rail. + * + * Both layouts are the same two elements — only the container's direction and the + * children's classes change — because `isCollapsed` flips in one frame while the + * rail takes 200ms to widen, so the row spends that window laid out at a width it + * does not fit in. Neither element may give ground there: the profile stops at its + * avatar (no `min-w-0`) and the help button never shrinks, so the row overflows the + * narrow rail and the aside's `overflow-hidden` clips it. The avatar keeps the exact + * position it holds collapsed, and the `?` rides in on the opening edge — paced by + * the rail itself rather than by a duration of its own. + * + * Collapsed reverses the flex direction instead of reordering the DOM, which is what + * keeps both elements (and the help menu's trigger) alive across a toggle. The cost + * is bottom-up focus order there, a smaller price for one pair of adjacent controls + * than remounting a trigger mid-animation. + */ +export function SidebarFooter({ + workspaceId, + isCollapsed, + showCollapsedTooltips, + onOpenSettings, + onOpenDocs, + onJoinSlack, + onContactSupport, +}: SidebarFooterProps) { + const { data: profile } = useUserProfile() + const { data: session } = useSession() + const hostContext = useWorkspaceHostContext() + const { isInvitationsDisabled } = useWorkspaceInvitePolicy(workspaceId) + + const name = profile ? profile.name?.trim() || profile.email : '' + + /** + * Subscription is dropped for viewers the Billing page would turn away — a + * deployment with billing off, or anyone who is not the payer (on an + * organization-hosted workspace, every member who is not an org admin). The + * settings sidebar hides its own Billing entry on exactly this test. + */ + const menuItems = PROFILE_MENU_ITEMS.filter( + (item) => + item.section !== 'billing' || canViewWorkspaceBillingSettings(hostContext, session?.user?.id) + ) + + /** + * Teammates is a dead end on a plan that cannot invite, so a blocked viewer is + * sent to the plan itself instead — which resolves to the upgrade page for + * anyone who cannot manage the payer. With billing off there is nowhere to send + * them and no upgrade to make, so the row simply does nothing. This is the gate + * the workspace switcher's "Manage workspace" entry carried before this menu + * took the section over. + */ + const handleSelectSection = (section: SettingsSection) => { + if (section === 'teammates' && isInvitationsDisabled) { + if (isBillingEnabled) onOpenSettings('billing') + return + } + onOpenSettings(section) + } + + /** + * Built from plain `img`/`div` rather than the emcn `Avatar`, whose Radix root + * renders a `` — and globals fade every `span` in the collapsed rail to + * `opacity: 0`, which blanked the avatar exactly where it is the only thing + * left to see. The workspace header's logo sidesteps the same rule the same way. + */ + const avatar = !profile ? ( + + ) : profile.image ? ( + + ) : ( +
+ {name.charAt(0).toUpperCase()} +
+ ) + + /** + * Expanded, the chip hugs its content (`max-w-full` so a long name truncates + * rather than overflowing) and the free width belongs to the wrapper it sits in, + * so hover highlights only the avatar and name. Collapsed, `fullWidth` fills the + * narrow rail instead. Both mirror the workspace header's chip exactly. + * + * No `min-w-0`: the label already truncates on its own, and letting the chip + * shrink past its avatar is what let the help button ride onto the photo while + * the rail was still narrow (see {@link SidebarFooter}). Its automatic minimum + * is exactly the icon-only chip, so the avatar holds the same spot at any width. + * + * The name is the button's accessible name — no `aria-label`, which would + * override the visible text. Radix contributes the menu role and expanded state. + */ + const profileMenu = ( + + + + + + + + {menuItems.map(({ section, label, icon: Icon }) => ( + handleSelectSection(section)}> + + {label} + + ))} + + + ) + + /** + * The same `Chip` the workspace header uses for Search and Collapse, so the two + * ends of the rail carry identical chrome — box, radius, hover fill, and glyph + * size all come from the component rather than a local class string. + * + * One node across both states; only `fullWidth` changes (collapsed it fills the + * rail, expanded it hugs its icon). Toggling a prop rather than rendering two + * buttons keeps the same DOM node — and the same Radix menu — alive through the + * transition instead of tearing one trigger down and mounting another mid-animation. + * + * Icon-only in both: the collapsed rail hides labels anyway. The tooltip names it + * while the rail is collapsed. + */ + const helpMenu = ( + + + + + + + {/* Anchored to whichever edge the trigger sits on, so the menu never overhangs the rail. */} + + + + Docs + + + + Join Slack + + + + Contact support + + + + ) + + return ( +
+ {/* Expanded, claims the row's free width so the help button lands hard right — + the same wrapper the workspace header puts around its chip. `flex` makes the + inline-flex chip a flex item rather than an inline one, so the wrapper is + exactly the chip's 30px instead of a line box padded by the strut's + half-leading, which would deepen the bar below the chip. Collapsed, it + stretches to the rail on its own and the chip fills it. */} +
{profileMenu}
+ {helpMenu} +
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/index.ts new file mode 100644 index 00000000000..225ee6fb60e --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/index.ts @@ -0,0 +1 @@ +export { SidebarSection } from './sidebar-section' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/sidebar-section.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/sidebar-section.tsx new file mode 100644 index 00000000000..341f8f8091c --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/sidebar-section/sidebar-section.tsx @@ -0,0 +1,119 @@ +'use client' + +import { type ReactNode, useState } from 'react' +import { ChevronDown, cn, Expandable, ExpandableContent } from '@sim/emcn' + +/** + * Title-row layout, shared by the toggle and its rail-collapsed counterpart so the + * label lands identically either way. The row's gutter lives here rather than on the + * row itself: the toggle stretches edge to edge and insets only its own content, so + * the full width of the rail is clickable rather than just the text and chevron. + */ +const TITLE_ROW_CLASS = 'flex h-full min-w-0 flex-1 items-center gap-2 px-4' + +/** + * One transition covering both of the chevron's states — the hover fade and the + * collapse rotation — so the two can never drift apart. 150ms on Tailwind's default + * `cubic-bezier(0.4, 0, 0.2, 1)` is the same curve the `collapsible-up`/`-down` + * keyframes use for the body, so the whole section animates as one gesture. + */ +const CHEVRON_TRANSITION_CLASS = 'transition-[opacity,transform] duration-150' + +interface SidebarSectionProps { + title: string + /** Controls pinned to the right of the title (e.g. the Workflows create/more buttons). */ + action?: ReactNode + /** + * True while the rail is collapsed. Only icons are visible there, so the title + * renders as a static label — no chevron, nothing focusable — and the content + * stays open regardless of what the user last toggled. + */ + railCollapsed?: boolean + /** Layout classes for the section wrapper (section gap, positioning). */ + className?: string + children: ReactNode +} + +/** + * A titled, collapsible group of sidebar items ("Chats", "Workspace", "Workflows", + * and each settings group). + * + * Owns the section's vertical rhythm so every section matches: an 18px header row + * — exactly the title's line box at `text-caption` — then a 6px gap to the content, + * sitting between the 2px item gap and the 16px section gap. The gap rides inside + * the collapsing content, so it closes along with it. Consumers supply only the + * item container. + * + * The title carries `sidebar-collapse-hide` so it fades out with the rail while the + * row keeps its height, holding the collapsed rail on the expanded rail's grid. + */ +export function SidebarSection({ + title, + action, + railCollapsed = false, + className, + children, +}: SidebarSectionProps) { + const [expanded, setExpanded] = useState(true) + /** + * Collapse animations are enabled only after the first user toggle, so sections + * render at full height on mount instead of replaying the open animation. + */ + const [animationsEnabled, setAnimationsEnabled] = useState(false) + + const handleToggle = () => { + setAnimationsEnabled(true) + setExpanded((prev) => !prev) + } + + const label = ( + + {title} + + ) + + return ( +
+
+ {railCollapsed ? ( +
{label}
+ ) : ( + + )} + {/* Carries the gutter the row gave up so the toggle can reach the rail's edge. */} + {action ?
{action}
: null} +
+ + + {/* The header gap pads an inner wrapper rather than the animated element: + `collapsible-up`/`-down` interpolate height alone, so a margin here would + hold its full 6px for the whole close and then vanish on unmount, snapping + the next section up. Padding on the content itself can't work either — + border-box keeps it rendered at `height: 0`, so the section never shuts. */} +
{children}
+
+
+
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx index 87bb0ad375b..98bb5442769 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx @@ -18,14 +18,14 @@ import { LogOut, Mail, Pencil, + Pin, + PinOff, Plus, SquareArrowUpRight, Trash, Unlock, - Workflow, X, } from '@sim/emcn/icons' -import { Pin, PinOff } from 'lucide-react' interface ContextMenuProps { isOpen: boolean @@ -33,7 +33,6 @@ interface ContextMenuProps { menuRef: React.RefObject onClose: () => void onOpenInNewTab?: () => void - onFindReferences?: () => void onMarkAsRead?: () => void onMarkAsUnread?: () => void onTogglePin?: () => void @@ -62,7 +61,6 @@ interface ContextMenuProps { */ onCloseTab?: () => void showOpenInNewTab?: boolean - showFindReferences?: boolean showMarkAsRead?: boolean showMarkAsUnread?: boolean showPin?: boolean @@ -104,7 +102,6 @@ export function ContextMenu({ menuRef, onClose, onOpenInNewTab, - onFindReferences, onMarkAsRead, onMarkAsUnread, onTogglePin, @@ -117,7 +114,6 @@ export function ContextMenu({ onDelete, onCloseTab, showOpenInNewTab = false, - showFindReferences = false, showMarkAsRead = false, showMarkAsUnread = false, showPin = false, @@ -148,8 +144,7 @@ export function ContextMenu({ showUploadLogo = false, disableUploadLogo = false, }: ContextMenuProps) { - const hasNavigationSection = - (showOpenInNewTab && onOpenInNewTab) || (showFindReferences && onFindReferences) + const hasNavigationSection = showOpenInNewTab && onOpenInNewTab const hasStatusSection = (showMarkAsRead && onMarkAsRead) || (showMarkAsUnread && onMarkAsUnread) || @@ -211,17 +206,6 @@ export function ContextMenu({ Open in new tab )} - {showFindReferences && onFindReferences && ( - { - onFindReferences() - onClose() - }} - > - - Show references - - )} {hasNavigationSection && (hasStatusSection || hasEditSection || hasCopySection) && ( )} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx index 9cec8e5a508..8d0d15b1544 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx @@ -2,12 +2,11 @@ import { memo, useCallback, useMemo, useRef, useState } from 'react' import { chipVariants, cn, toast } from '@sim/emcn' -import { Lock } from '@sim/emcn/icons' +import { ChevronRight, Folder, FolderOpen, Lock, MoreHorizontal } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' import clsx from 'clsx' -import { ChevronRight, Folder, FolderOpen, MoreHorizontal } from 'lucide-react' import { useRouter } from 'next/navigation' import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types' import { generateSubfolderName } from '@/lib/workspaces/naming' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/components/reference-tree/reference-tree.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/components/reference-tree/reference-tree.tsx deleted file mode 100644 index ae4def07afa..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/components/reference-tree/reference-tree.tsx +++ /dev/null @@ -1,68 +0,0 @@ -'use client' - -import { Workflow } from '@sim/emcn/icons' -import type { ReferenceNode } from '@/lib/api/contracts/workflow-references' - -const CONFIG = { - /** Horizontal indent added per tree level, in pixels. */ - INDENT_PER_LEVEL: 16, - /** Base row padding: lands depth-0 content on the modal's px-4 text gutter. */ - BASE_INDENT: 8, -} as const - -interface ReferenceTreeProps { - nodes: ReferenceNode[] - /** Invoked with a workflow id when a row is activated. */ - onNavigate: (workflowId: string) => void -} - -interface ReferenceTreeItemProps { - node: ReferenceNode - depth: number - onNavigate: (workflowId: string) => void -} - -function ReferenceTreeItem({ node, depth, onNavigate }: ReferenceTreeItemProps) { - return ( -
- - {node.children.length > 0 && ( -
- {node.children.map((child) => ( - - ))} -
- )} -
- ) -} - -/** - * Read-only recursive tree of workflow references. Each row navigates to its - * workflow on click; cyclic leaves are marked and render no children. - */ -export function ReferenceTree({ nodes, onNavigate }: ReferenceTreeProps) { - return ( -
- {nodes.map((node) => ( - - ))} -
- ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/references-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/references-modal.tsx deleted file mode 100644 index e507111c956..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/references-modal.tsx +++ /dev/null @@ -1,74 +0,0 @@ -'use client' - -import { useState } from 'react' -import { ChipModal, ChipModalBody, ChipModalHeader, ChipModalTabs } from '@sim/emcn' -import { useRouter } from 'next/navigation' -import { ReferenceTree } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/components/reference-tree/reference-tree' -import { useWorkflowReferences } from '@/hooks/queries/workflow-references' - -type ReferencesTab = 'callers' | 'callees' - -const TABS = [ - { value: 'callers', label: 'Used by' }, - { value: 'callees', label: 'Uses' }, -] as const - -const EMPTY_MESSAGE: Record = { - callers: 'No workflows call this workflow.', - callees: "This workflow doesn't call any other workflows.", -} - -interface ReferencesModalProps { - onClose: () => void - workspaceId: string - workflowId: string - workflowName: string -} - -/** - * IDE-style reference viewer for a workflow. "Used by" lists the workflows that - * call it (inbound); "Uses" lists the workflows it calls (outbound). Both are - * recursive trees whose rows navigate to the referenced workflow. Mounted on - * demand by the owning row, so state initializes fresh per open. - */ -export function ReferencesModal({ - onClose, - workspaceId, - workflowId, - workflowName, -}: ReferencesModalProps) { - const router = useRouter() - const [activeTab, setActiveTab] = useState('callers') - - const { data, isPending, isError } = useWorkflowReferences(workflowId) - - const handleNavigate = (targetId: string) => { - router.push(`/workspace/${workspaceId}/w/${targetId}`) - onClose() - } - - const nodes = data?.[activeTab] ?? [] - - return ( - !next && onClose()} srTitle='References'> - References — {workflowName} - - setActiveTab(value as ReferencesTab)} - aria-label='Reference direction' - /> - {isPending ? ( -

Loading references…

- ) : isError ? ( -

Failed to load references.

- ) : nodes.length === 0 ? ( -

{EMPTY_MESSAGE[activeTab]}

- ) : ( - - )} -
-
- ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx index 372d093b014..5eb559647d7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/workflow-item.tsx @@ -2,15 +2,13 @@ import { memo, useCallback, useMemo, useRef, useState } from 'react' import { chipVariants, cn } from '@sim/emcn' -import { Lock } from '@sim/emcn/icons' +import { Lock, MoreHorizontal } from '@sim/emcn/icons' import clsx from 'clsx' -import { MoreHorizontal } from 'lucide-react' import Link from 'next/link' import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu' import { DeleteModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/delete-modal/delete-modal' -import { ReferencesModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/references-modal/references-modal' import { Avatars } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/workflow-item/avatars/avatars' import { useContextMenu, @@ -81,7 +79,6 @@ export const WorkflowItem = memo(function WorkflowItem({ const { canDeleteWorkflows, canDeleteFolder } = useCanDelete({ workspaceId }) const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) - const [isReferencesOpen, setIsReferencesOpen] = useState(false) const [deleteItemType, setDeleteItemType] = useState<'workflow' | 'mixed'>('workflow') const [deleteModalNames, setDeleteModalNames] = useState('') const [canDeleteSelection, setCanDeleteSelection] = useState(true) @@ -400,10 +397,6 @@ export const WorkflowItem = memo(function WorkflowItem({ [shouldPreventClickRef, workflow.id, onWorkflowClick, isEditing] ) - const handleFindReferences = useCallback(() => { - setIsReferencesOpen(true) - }, []) - return ( <> - - {isReferencesOpen && ( - setIsReferencesOpen(false)} - workspaceId={workspaceId} - workflowId={workflow.id} - workflowName={workflow.name} - /> - )} ) }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/components/pending-invitations/view-invitations-menu-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/components/pending-invitations/view-invitations-menu-item.tsx index 4fe2657eff8..c0d1e12e5c2 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/components/pending-invitations/view-invitations-menu-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/components/pending-invitations/view-invitations-menu-item.tsx @@ -22,7 +22,7 @@ export function ViewInvitationsMenuItem({ onOpen }: ViewInvitationsMenuItemProps } return ( - + View invitations ) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx index 2357a7a4e42..988f3b29c56 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workspace-header/workspace-header.tsx @@ -2,10 +2,11 @@ import { memo, type ReactElement, useEffect, useRef, useState } from 'react' import { - ChevronDown, Chip, + ChipChevronDown, ChipConfirmModal, ChipInput, + chipContentLabelClass, chipGeometryClass, chipVariants, cn, @@ -18,10 +19,9 @@ import { Skeleton, Tooltip, } from '@sim/emcn' -import { ManageWorkspace, PanelLeft } from '@sim/emcn/icons' +import { MoreHorizontal, PanelLeft, Search } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { useQueryClient } from '@tanstack/react-query' -import { MoreHorizontal, Search } from 'lucide-react' import { useActiveOrganization } from '@/lib/auth/auth-client' import { isBillingEnabled } from '@/lib/core/config/env-flags' import { InviteModal } from '@/app/workspace/[workspaceId]/components/invite-modal' @@ -266,6 +266,11 @@ function WorkspaceHeaderImpl({ * server refused the send. */ const { userPermissions } = useWorkspacePermissionsContext() + /** + * Derived from the `workspaces` prop rather than {@link useWorkspaceInvitePolicy}: + * this component is already handed the list it would otherwise re-read, and the + * same object supplies the logo, color, and organization below. + */ const inviteDisabledReason = activeWorkspaceFull?.inviteDisabledReason ?? null const isInvitationsDisabled = isInvitationsDisabledByConfig || inviteDisabledReason !== null const createWorkspaceTarget: CreateWorkspaceTarget = @@ -509,10 +514,8 @@ function WorkspaceHeaderImpl({ )} {!isCollapsed && activeWorkspace?.name && ( <> - - {activeWorkspace.name} - - + {activeWorkspace.name} + )} @@ -611,9 +614,7 @@ function WorkspaceHeaderImpl({ } > {editingWorkspaceId === workspace.id ? ( -
+
{workspace.logoUrl ? ( -
- - - - { - setIsWorkspaceMenuOpen(false) - if (isInvitationsDisabled) { - if (isBillingEnabled) navigateToSettings({ section: 'billing' }) - return - } - setIsInviteModalOpen(true) - }} - fullWidth - flush - className='select-none' - > - Invite teammates - - - { - setIsWorkspaceMenuOpen(false) - setIsViewInvitationsOpen(true) - }} - /> - - { + + { + setIsWorkspaceMenuOpen(false) + if (isInvitationsDisabled) { + if (isBillingEnabled) navigateToSettings({ section: 'billing' }) + return + } + setIsInviteModalOpen(true) + }} + fullWidth + className='select-none' + > + Invite teammates + + + { setIsWorkspaceMenuOpen(false) - if (isInvitationsDisabled) { - if (isBillingEnabled) navigateToSettings({ section: 'billing' }) - return - } - navigateToSettings({ section: 'teammates' }) + setIsViewInvitationsOpen(true) }} - fullWidth - flush - className='select-none' - > - Manage workspace - - + /> +
)} @@ -830,11 +808,10 @@ function WorkspaceHeaderImpl({ diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/constants.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/constants.ts index 88a25c6de88..1b2df2b4460 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/constants.ts @@ -6,11 +6,29 @@ * rhythm changes, update these values and every consumer follows. */ -/** Vertical gap between sibling sidebar sections (12px). */ -export const SIDEBAR_SECTION_GAP_CLASS = 'mt-3' +/** Vertical gap between sibling sidebar sections (16px). */ +export const SIDEBAR_SECTION_GAP_CLASS = 'mt-4' -/** Vertical gap between items within a sidebar section (2px). */ -export const SIDEBAR_ITEM_GAP_CLASS = 'gap-0.5' +/** + * Vertical gap between items within a sidebar section (1px). + * + * Written as an arbitrary value, not `gap-px`: the `px` spacing key is remapped + * to `--border-width`, which thins to 0.5px on hidpi displays so hairline borders + * stay hairlines. That is right for a rule and wrong for a gap — this one is a + * literal pixel at every density. + */ +export const SIDEBAR_ITEM_GAP_CLASS = 'gap-[1px]' + +/** + * Halves of {@link SIDEBAR_SECTION_GAP_CLASS} straddling the scroll region's + * divider: the pinned block above carries the top half, the scroll region below + * carries the bottom half. Split this way the divider sits centered in a gap that + * reads as one section gap, so the first section header is spaced from the block + * above it exactly like every other section boundary. Keep both in step with the + * section gap. + */ +export const SIDEBAR_DIVIDER_PAD_ABOVE_CLASS = 'pb-2' +export const SIDEBAR_DIVIDER_PAD_BELOW_CLASS = 'pt-2' /** * Nested-selector variants for cmdk-based surfaces (e.g. the search modal). @@ -18,7 +36,7 @@ export const SIDEBAR_ITEM_GAP_CLASS = 'gap-0.5' */ /** Matches {@link SIDEBAR_SECTION_GAP_CLASS} applied to adjacent cmdk groups. */ -export const CMDK_SECTION_GAP_CLASS = '[&_[cmdk-group]+[cmdk-group]]:mt-3' +export const CMDK_SECTION_GAP_CLASS = '[&_[cmdk-group]+[cmdk-group]]:mt-4' /** Matches {@link SIDEBAR_ITEM_GAP_CLASS} applied to cmdk item containers. */ -export const CMDK_ITEM_GAP_CLASS = '[&_[cmdk-group-items]]:gap-0.5' +export const CMDK_ITEM_GAP_CLASS = '[&_[cmdk-group-items]]:gap-[1px]' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 0fcce6e1138..3495d548a25 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -20,26 +20,22 @@ import { Upload, } from '@sim/emcn' import { - BookOpen, - Calendar, Database, Files, - HelpCircle, Integration, + MoreHorizontal, PanelLeft, + Pin, Plus, Search, - Settings, Table, Task, Workflow, } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' -import { MoreHorizontal, Pin } from 'lucide-react' import Link from 'next/link' import { useParams, usePathname, useRouter } from 'next/navigation' import { usePostHog } from 'posthog-js/react' -import { SlackIcon } from '@/components/icons' import { useSession } from '@/lib/auth/auth-client' import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types' import { isMacPlatform } from '@/lib/core/utils/platform' @@ -47,6 +43,7 @@ import { buildFolderTree, getFolderPath } from '@/lib/folders/tree' import { captureEvent } from '@/lib/posthog/client' import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' +import type { SettingsSection } from '@/app/workspace/[workspaceId]/settings/navigation' import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils' import { CollapsedChatFlyoutItem, @@ -57,6 +54,8 @@ import { NavItemContextMenu, SearchModal, SettingsSidebar, + SidebarFooter, + SidebarSection, WorkflowList, WorkspaceHeader, } from '@/app/workspace/[workspaceId]/w/components/sidebar/components' @@ -67,6 +66,8 @@ import { import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu' import { DeleteModal } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/delete-modal/delete-modal' import { + SIDEBAR_DIVIDER_PAD_ABOVE_CLASS, + SIDEBAR_DIVIDER_PAD_BELOW_CLASS, SIDEBAR_ITEM_GAP_CLASS, SIDEBAR_SECTION_GAP_CLASS, } from '@/app/workspace/[workspaceId]/w/components/sidebar/constants' @@ -143,9 +144,10 @@ export function SidebarTooltip({ ) } +/** Stands in for a chip row while a list loads, so it carries no margin either. */ function SidebarItemSkeleton() { return ( -
+
) @@ -256,7 +258,7 @@ const SidebarChatItem = memo(function SidebarChatItem({ isMenuOpen && 'opacity-100' )} > - +
)} @@ -343,6 +345,13 @@ export const SIDEBAR_SCROLL_EVENT = 'sidebar-scroll-to-item' const HIDDEN_STYLE = { display: 'none' } as const +/** + * Opts a control out of the desktop shell's window-drag region. The header row is + * draggable chrome, so anything clickable inside it has to say so or the click is + * swallowed by the drag handler. + */ +const DRAG_EXEMPT_CLASS = '[-webkit-app-region:no-drag]' + /** * Sidebar component with resizable width that persists across page refreshes. * @@ -392,7 +401,7 @@ export const Sidebar = memo(function Sidebar({ const { data: sessionData, isPending: sessionLoading } = useSession() const { canEdit, isLoading: permissionsLoading } = useUserPermissionsContext() const { config: permissionConfig, filterBlocks } = usePermissionConfig() - const { navigateToSettings, getSettingsHref } = useSettingsNavigation() + const { navigateToSettings } = useSettingsNavigation() const initializeSearchData = useSearchModalStore((state) => state.initializeData) const customBlockOverlayVersion = useCustomBlockOverlayVersion() const providers = useProvidersStore((state) => state.providers) @@ -738,22 +747,18 @@ export const Sidebar = memo(function Sidebar({ icon: Home, href: `/workspace/${workspaceId}/home`, }, - { - id: 'search', - label: 'Search', - icon: Search, - onClick: openSearchModal, - }, { id: 'integrations', label: 'Integrations', icon: Integration, href: `/workspace/${workspaceId}/integrations`, + /* Skills is a tab of this surface, not its own nav item — keep the entry + lit while the user is on it. */ additionalActivePaths: [`/workspace/${workspaceId}/skills`], hidden: permissionConfig.hideIntegrationsTab, }, ].filter((item) => !item.hidden), - [workspaceId, openSearchModal, permissionConfig.hideIntegrationsTab] + [workspaceId, permissionConfig.hideIntegrationsTab] ) const workspaceNavItems = useMemo( @@ -780,12 +785,6 @@ export const Sidebar = memo(function Sidebar({ href: `/workspace/${workspaceId}/knowledge`, hidden: permissionConfig.hideKnowledgeBaseTab, }, - { - id: 'scheduled-tasks', - label: 'Scheduled tasks', - icon: Calendar, - href: `/workspace/${workspaceId}/scheduled-tasks`, - }, { id: 'logs', label: 'Logs', @@ -801,22 +800,14 @@ export const Sidebar = memo(function Sidebar({ ] ) - const footerItems = useMemo( - () => [ - { - id: 'settings', - label: 'Settings', - icon: Settings, - href: getSettingsHref(), - onClick: () => { - if (!isCollapsedRef.current) { - setSidebarWidth(SIDEBAR_WIDTH.MIN) - } - navigateToSettings() - }, - }, - ], - [navigateToSettings, getSettingsHref, setSidebarWidth] + const handleOpenSettings = useCallback( + (section: SettingsSection) => { + if (!isCollapsedRef.current) { + setSidebarWidth(SIDEBAR_WIDTH.MIN) + } + navigateToSettings({ section }) + }, + [navigateToSettings, setSidebarWidth] ) const { data: fetchedChats = [], isLoading: chatsLoading } = useMothershipChats(workspaceId) @@ -1010,7 +1001,6 @@ export const Sidebar = memo(function Sidebar({ ) const [hasOverflowTop, setHasOverflowTop] = useState(false) - const [hasOverflowBottom, setHasOverflowBottom] = useState(false) useEffect(() => { const container = scrollContainerRef.current @@ -1018,9 +1008,6 @@ export const Sidebar = memo(function Sidebar({ const updateScrollState = () => { setHasOverflowTop(container.scrollTop > 1) - setHasOverflowBottom( - container.scrollHeight > container.scrollTop + container.clientHeight + 1 - ) } updateScrollState() @@ -1327,25 +1314,57 @@ export const Sidebar = memo(function Sidebar({ isCollapsed={isCollapsed} onExpandSidebar={toggleCollapsed} /> - - - + + +
{isOnSettingsPage ? ( @@ -1359,7 +1378,8 @@ export const Sidebar = memo(function Sidebar({ className={cn( SIDEBAR_SECTION_GAP_CLASS, SIDEBAR_ITEM_GAP_CLASS, - 'flex flex-shrink-0 flex-col px-2 pb-1.5' + SIDEBAR_DIVIDER_PAD_ABOVE_CLASS, + 'flex flex-shrink-0 flex-col px-2' )} > {topNavItems.map((item) => ( @@ -1376,21 +1396,22 @@ export const Sidebar = memo(function Sidebar({
-
-
-
Chats
-
+ {isCollapsed ? ( {chatsLoading ? ( @@ -1421,7 +1442,7 @@ export const Sidebar = memo(function Sidebar({ )} ) : ( -
+
{chatsLoading ? ( ) : ( @@ -1500,12 +1521,13 @@ export const Sidebar = memo(function Sidebar({ )}
)} -
+
-
-
-
Workspace
-
+
{workspaceNavItems.map((item) => ( ))}
-
- -
-
-
Workflows
- {!isCollapsed && ( + + + @@ -1535,13 +1554,13 @@ export const Sidebar = memo(function Sidebar({ @@ -1575,7 +1594,7 @@ export const Sidebar = memo(function Sidebar({
- )} -
+ ) + } + > {isCollapsed ? ( {workflowsLoading && regularWorkflows.length === 0 ? ( @@ -1656,7 +1675,7 @@ export const Sidebar = memo(function Sidebar({ )} ) : ( -
+
{workflowsLoading && regularWorkflows.length === 0 ? ( ) : ( @@ -1674,58 +1693,19 @@ export const Sidebar = memo(function Sidebar({ )}
)} -
+
-
- - - - - - - - - - Docs - - - - Slack Community - - - - Report an issue - - - - - {footerItems.map((item) => ( - - ))} -
+ ) => createElement(MessageCircle, props) - export const ChatTriggerBlock: BlockConfig = { type: 'chat_trigger', triggerAllowed: true, @@ -18,7 +14,7 @@ export const ChatTriggerBlock: BlockConfig = { hideFromToolbar: true, sunset: { status: 'legacy', replacedBy: 'start_trigger' }, bgColor: '#6F3DFA', - icon: ChatTriggerIcon, + icon: Task, subBlocks: [], tools: { access: [], diff --git a/apps/sim/blocks/blocks/clickhouse.ts b/apps/sim/blocks/blocks/clickhouse.ts index 8f09b3934f4..3bfd6735fb9 100644 --- a/apps/sim/blocks/blocks/clickhouse.ts +++ b/apps/sim/blocks/blocks/clickhouse.ts @@ -1,13 +1,4 @@ -import { - Bell, - ClipboardList, - Database, - File, - Search, - Server, - TrashOutline, - Wrench, -} from '@sim/emcn/icons' +import { Bell, ClipboardList, Database, File, Search, Server, Trash, Wrench } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' import { ClickHouseIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' @@ -525,7 +516,7 @@ export const ClickHouseBlockMeta = { tags: ['data-warehouse', 'maintenance'], }, { - icon: TrashOutline, + icon: Trash, title: 'ClickHouse partition cleanup', prompt: 'Build a scheduled workflow that enforces a retention policy on my ClickHouse events table: take an explicit cutoff date as input, list the table partitions, select only the partitions on that exact table whose range ends strictly before the cutoff, and drop just those. Never infer the cutoff and never drop a partition that is not clearly past it.', diff --git a/apps/sim/blocks/blocks/generic_webhook.ts b/apps/sim/blocks/blocks/generic_webhook.ts index 6d83b3860ed..6aa5b8de995 100644 --- a/apps/sim/blocks/blocks/generic_webhook.ts +++ b/apps/sim/blocks/blocks/generic_webhook.ts @@ -1,17 +1,13 @@ -import type { SVGProps } from 'react' -import { createElement } from 'react' -import { Webhook } from 'lucide-react' +import { Webhook } from '@sim/emcn/icons' import type { BlockConfig } from '@/blocks/types' import { getTrigger } from '@/triggers' -const WebhookIcon = (props: SVGProps) => createElement(Webhook, props) - export const GenericWebhookBlock: BlockConfig = { type: 'generic_webhook', name: 'Webhook Trigger', description: 'Receive webhooks from any service by configuring a custom webhook.', category: 'triggers', - icon: WebhookIcon, + icon: Webhook, bgColor: '#10B981', // Green color for triggers docsLink: 'https://docs.sim.ai/workflows/triggers/webhook', triggerAllowed: true, diff --git a/apps/sim/blocks/blocks/input_trigger.ts b/apps/sim/blocks/blocks/input_trigger.ts index c2891e5fa58..e7a5675fd8a 100644 --- a/apps/sim/blocks/blocks/input_trigger.ts +++ b/apps/sim/blocks/blocks/input_trigger.ts @@ -1,10 +1,6 @@ -import type { SVGProps } from 'react' -import { createElement } from 'react' -import { FormInput } from 'lucide-react' +import { FormInput } from '@sim/emcn/icons' import type { BlockConfig } from '@/blocks/types' -const InputTriggerIcon = (props: SVGProps) => createElement(FormInput, props) - export const InputTriggerBlock: BlockConfig = { type: 'input_trigger', triggerAllowed: true, @@ -21,7 +17,7 @@ export const InputTriggerBlock: BlockConfig = { hideFromToolbar: true, sunset: { status: 'legacy', replacedBy: 'start_trigger' }, bgColor: '#3B82F6', - icon: InputTriggerIcon, + icon: FormInput, subBlocks: [ { id: 'inputFormat', diff --git a/apps/sim/blocks/blocks/manual_trigger.ts b/apps/sim/blocks/blocks/manual_trigger.ts index 6ba153886f6..3194a100dbb 100644 --- a/apps/sim/blocks/blocks/manual_trigger.ts +++ b/apps/sim/blocks/blocks/manual_trigger.ts @@ -1,10 +1,6 @@ -import type { SVGProps } from 'react' -import { createElement } from 'react' -import { Play } from 'lucide-react' +import { Play } from '@sim/emcn/icons' import type { BlockConfig } from '@/blocks/types' -const ManualTriggerIcon = (props: SVGProps) => createElement(Play, props) - export const ManualTriggerBlock: BlockConfig = { type: 'manual_trigger', triggerAllowed: true, @@ -20,7 +16,7 @@ export const ManualTriggerBlock: BlockConfig = { hideFromToolbar: true, sunset: { status: 'legacy', replacedBy: 'start_trigger' }, bgColor: '#2563EB', - icon: ManualTriggerIcon, + icon: Play, subBlocks: [], tools: { access: [], diff --git a/apps/sim/blocks/blocks/schedule.ts b/apps/sim/blocks/blocks/schedule.ts index a65ac58792f..7dbda3c7fd4 100644 --- a/apps/sim/blocks/blocks/schedule.ts +++ b/apps/sim/blocks/blocks/schedule.ts @@ -1,10 +1,6 @@ -import type { SVGProps } from 'react' -import { createElement } from 'react' -import { Clock } from 'lucide-react' +import { Clock } from '@sim/emcn/icons' import type { BlockConfig } from '@/blocks/types' -const ScheduleIcon = (props: SVGProps) => createElement(Clock, props) - export const ScheduleBlock: BlockConfig = { type: 'schedule', triggerAllowed: true, @@ -19,7 +15,7 @@ export const ScheduleBlock: BlockConfig = { `, category: 'triggers', bgColor: '#6366F1', - icon: ScheduleIcon, + icon: Clock, subBlocks: [ { diff --git a/apps/sim/blocks/blocks/sftp.ts b/apps/sim/blocks/blocks/sftp.ts index 5e5d1928a9d..dc2fc15e224 100644 --- a/apps/sim/blocks/blocks/sftp.ts +++ b/apps/sim/blocks/blocks/sftp.ts @@ -1,12 +1,4 @@ -import { - ClipboardList, - Download, - File, - Search, - Server, - TrashOutline, - Upload, -} from '@sim/emcn/icons' +import { ClipboardList, Download, File, Search, Server, Trash, Upload } from '@sim/emcn/icons' import { SftpIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' @@ -352,7 +344,7 @@ export const SftpBlockMeta = { tags: ['files', 'sync', 'sftp'], }, { - icon: TrashOutline, + icon: Trash, title: 'Archive and delete old SFTP files', prompt: 'Build a workflow that runs nightly, lists an SFTP directory, downloads files older than a threshold to archive them, and then deletes the originals from the remote server.', diff --git a/apps/sim/blocks/blocks/wait.ts b/apps/sim/blocks/blocks/wait.ts index a3f6f56bf35..46012672b17 100644 --- a/apps/sim/blocks/blocks/wait.ts +++ b/apps/sim/blocks/blocks/wait.ts @@ -1,10 +1,6 @@ -import type { SVGProps } from 'react' -import { createElement } from 'react' -import { PauseCircle } from 'lucide-react' +import { CirclePause } from '@sim/emcn/icons' import type { BlockConfig } from '@/blocks/types' -const WaitIcon = (props: SVGProps) => createElement(PauseCircle, props) - export const WaitBlock: BlockConfig = { type: 'wait', name: 'Wait', @@ -19,7 +15,7 @@ export const WaitBlock: BlockConfig = { `, category: 'blocks', bgColor: '#F59E0B', - icon: WaitIcon, + icon: CirclePause, docsLink: 'https://docs.sim.ai/workflows/blocks/wait', subBlocks: [ { diff --git a/apps/sim/blocks/custom/custom-block-icon.tsx b/apps/sim/blocks/custom/custom-block-icon.tsx index 4151fa13284..f14ef79ed88 100644 --- a/apps/sim/blocks/custom/custom-block-icon.tsx +++ b/apps/sim/blocks/custom/custom-block-icon.tsx @@ -2,7 +2,7 @@ import { memo, type SVGProps } from 'react' import { cn } from '@sim/emcn' -import { Box } from 'lucide-react' +import { Box } from '@sim/emcn/icons' import type { BlockIcon } from '@/blocks/types' const cache = new Map() @@ -41,8 +41,7 @@ export function makeImageIcon(url: string): BlockIcon { } /** Fallback icon for custom blocks published without an uploaded image. */ -// double-cast-allowed: a lucide icon component fills the SVG-typed BlockIcon slot -export const DefaultCustomBlockIcon: BlockIcon = Box as unknown as BlockIcon +export const DefaultCustomBlockIcon: BlockIcon = Box /** * Resolve a custom block's icon: the uploaded image, else the org's whitelabel logo diff --git a/apps/sim/components.json b/apps/sim/components.json index eea8c1fbb86..5c03e841bc2 100644 --- a/apps/sim/components.json +++ b/apps/sim/components.json @@ -17,6 +17,5 @@ "lib": "@/lib", "hooks": "@/hooks", "blocks": "@/blocks" - }, - "iconLibrary": "lucide" + } } diff --git a/apps/sim/components/agent-stream/agent-stream-chrome.tsx b/apps/sim/components/agent-stream/agent-stream-chrome.tsx index 5c34c1dd25a..ff7b3b832a0 100644 --- a/apps/sim/components/agent-stream/agent-stream-chrome.tsx +++ b/apps/sim/components/agent-stream/agent-stream-chrome.tsx @@ -2,7 +2,7 @@ import { useEffect, useLayoutEffect, useRef, useState } from 'react' import { cn } from '@sim/emcn' -import { Check, ChevronDown, Circle, Square, X } from 'lucide-react' +import { Check, ChevronDown, Circle, Square, X } from '@sim/emcn/icons' import type { AgentStreamToolCall, AgentStreamToolStatus, @@ -129,7 +129,6 @@ export function AgentStreamThinkingChrome({ 'size-[14px] transition-transform duration-150', open ? 'rotate-0' : '-rotate-90' )} - strokeWidth={2} /> {isStreaming ? ( + return } if (status === 'error') { - return + return } if (status === 'cancelled') { - return + return } - return + return } export interface AgentStreamToolCallsChromeProps { @@ -224,7 +223,6 @@ export function AgentStreamToolCallsChrome({ > {isStreaming ? 'Using tools…' : 'Tools'} diff --git a/apps/sim/components/permissions/member-row.tsx b/apps/sim/components/permissions/member-row.tsx index 8a235584204..8e184e5633a 100644 --- a/apps/sim/components/permissions/member-row.tsx +++ b/apps/sim/components/permissions/member-row.tsx @@ -81,7 +81,7 @@ export function MemberRow({ /> {canManage && ( - + Remove )} diff --git a/apps/sim/components/pii/custom-patterns-editor.tsx b/apps/sim/components/pii/custom-patterns-editor.tsx index 4be1204f5b0..f441e048cd1 100644 --- a/apps/sim/components/pii/custom-patterns-editor.tsx +++ b/apps/sim/components/pii/custom-patterns-editor.tsx @@ -1,7 +1,7 @@ 'use client' import { Chip, ChipInput } from '@sim/emcn' -import { Plus, Trash2 } from 'lucide-react' +import { Plus, Trash } from '@sim/emcn/icons' import type { CustomPiiPattern } from '@/lib/guardrails/pii-entities' import { validateRegexPattern } from '@/lib/guardrails/validate_regex' @@ -71,7 +71,7 @@ export function CustomPatternsEditor({ patterns, onChange }: CustomPatternsEdito onClick={() => removeRow(index)} className='flex size-[30px] flex-shrink-0 items-center justify-center rounded-md text-[var(--text-icon)] transition-colors hover-hover:bg-[var(--surface-active)] hover-hover:text-[var(--text-error)]' > - +
{error && {error}} diff --git a/apps/sim/components/settings/navigation.ts b/apps/sim/components/settings/navigation.ts index 3557160e1d1..8d93ff948e4 100644 --- a/apps/sim/components/settings/navigation.ts +++ b/apps/sim/components/settings/navigation.ts @@ -2,6 +2,7 @@ import type { ComponentType } from 'react' import { ClipboardList, Clock, + Credit, Cursor, Database, HexSimple, @@ -17,9 +18,8 @@ import { ShieldCheck, Shuffle, TerminalWindow, - TrashOutline, + Trash, Upload, - User, Users, Wrench, } from '@sim/emcn/icons' @@ -467,7 +467,7 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[] }, { label: 'Billing', - icon: ClipboardList, + icon: Credit, unified: { id: 'billing', description: 'Manage your plan, pricing, and invoices.', @@ -497,7 +497,7 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[] }, { label: 'Teammates', - icon: User, + icon: Users, unified: { id: 'teammates', description: 'Manage your teammates in this workspace.', @@ -658,7 +658,7 @@ export const SETTINGS_SECTION_REGISTRY: readonly SettingsSectionRegistryEntry[] }, { label: 'Recently deleted', - icon: TrashOutline, + icon: Trash, unified: { id: 'recently-deleted', description: 'Restore items deleted in the last 30 days.', diff --git a/apps/sim/components/ui/generated-password-input.tsx b/apps/sim/components/ui/generated-password-input.tsx index 9993878d60d..a27c8e039db 100644 --- a/apps/sim/components/ui/generated-password-input.tsx +++ b/apps/sim/components/ui/generated-password-input.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { Button, ChipInput, Tooltip } from '@sim/emcn' -import { Check, Clipboard, Eye, EyeOff, RefreshCw } from 'lucide-react' +import { Check, Clipboard, Eye, EyeOff, RefreshCw } from '@sim/emcn/icons' import { generatePassword } from '@/lib/core/security/encryption' interface GeneratedPasswordInputProps { diff --git a/apps/sim/components/ui/select.tsx b/apps/sim/components/ui/select.tsx index 3c22d4a2978..6cda934d922 100644 --- a/apps/sim/components/ui/select.tsx +++ b/apps/sim/components/ui/select.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import * as SelectPrimitive from '@radix-ui/react-select' import { cn } from '@sim/emcn' -import { Check, ChevronDown, ChevronUp } from 'lucide-react' +import { Check, ChevronDown, ChevronUp } from '@sim/emcn/icons' const Select = SelectPrimitive.Root diff --git a/apps/sim/content/blog/enterprise/components.tsx b/apps/sim/content/blog/enterprise/components.tsx index 1cfa15a6ebd..e273e4c0c19 100644 --- a/apps/sim/content/blog/enterprise/components.tsx +++ b/apps/sim/content/blog/enterprise/components.tsx @@ -1,7 +1,7 @@ 'use client' import { useState } from 'react' -import { ArrowRight, ChevronRight } from 'lucide-react' +import { ArrowRight, ChevronRight } from '@sim/emcn/icons' interface ContactButtonProps { href: string @@ -32,7 +32,7 @@ export function ContactButton({ href, children }: ContactButtonProps) { paddingLeft: '9px', paddingRight: '9px', fontSize: '13.5px', - fontWeight: 430, + fontWeight: 400, color: '#000000', textDecoration: 'none', transition: 'background 200ms, border-color 200ms', diff --git a/apps/sim/ee/access-control/components/access-control.tsx b/apps/sim/ee/access-control/components/access-control.tsx index ce4330347b1..2236236aeac 100644 --- a/apps/sim/ee/access-control/components/access-control.tsx +++ b/apps/sim/ee/access-control/components/access-control.tsx @@ -12,9 +12,9 @@ import { ChipTag, Label, } from '@sim/emcn' +import { ArrowRight, Plus } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { ArrowRight, Plus } from 'lucide-react' import { useParams } from 'next/navigation' import { useQueryState } from 'nuqs' import { isEnterprise } from '@/lib/billing/plan-helpers' diff --git a/apps/sim/ee/access-control/components/group-detail.tsx b/apps/sim/ee/access-control/components/group-detail.tsx index 1c1f19b92c8..4ef1cd6b7bc 100644 --- a/apps/sim/ee/access-control/components/group-detail.tsx +++ b/apps/sim/ee/access-control/components/group-detail.tsx @@ -22,11 +22,10 @@ import { Switch, toast, } from '@sim/emcn' -import { ArrowLeft } from '@sim/emcn/icons' +import { ArrowLeft, ChevronDown, Plus } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' import { formatDate } from '@sim/utils/formatting' -import { ChevronDown, Plus } from 'lucide-react' import { useQueryState } from 'nuqs' import type { ShareAuthType } from '@/lib/api/contracts/public-shares' import { isBlockTypeAccessControlExempt } from '@/lib/permission-groups/block-access' @@ -120,19 +119,16 @@ function matchesStatusFilter(filter: StatusFilter, enabled: boolean) { interface StatusFilterChipProps { value: StatusFilter onChange: (value: StatusFilter) => void - /** Set when the chip is the last control in its row, so it sits flush to the edge. */ - flush?: boolean } /** The All/Enabled/Disabled narrowing control shared by the three list tabs. */ -function StatusFilterChip({ value, onChange, flush }: StatusFilterChipProps) { +function StatusFilterChip({ value, onChange }: StatusFilterChipProps) { return ( onChange(next as StatusFilter)} options={STATUS_FILTER_OPTIONS} matchTriggerWidth={false} - flush={flush} className='w-[140px] flex-shrink-0' /> ) @@ -151,8 +147,6 @@ interface AuthModeFieldProps { * disables together with the toggle that owns it. The left padding lines both * children up with the parent's label text — row gutter (8) + checkbox (16) + * gap (8) = 32 — so the field reads as subordinate rather than as a sibling row. - * The dropdown is `flush` so its own `mx-0.5` doesn't push it 2px past the - * caption above it. */ function AuthModeField({ label, value, onChange, options, disabled }: AuthModeFieldProps) { const labelId = useId() @@ -164,7 +158,6 @@ function AuthModeField({ label, value, onChange, options, disabled }: AuthModeFi void setStatusFilter(next)} /> setProvidersAllowed(filteredProviders, !filteredProvidersAllAllowed)} disabled={filteredProviders.length === 0} > @@ -1754,7 +1746,6 @@ export function GroupDetail({ void setStatusFilter(next)} - flush />
{filteredCoreBlocks.length === 0 && filteredToolBlocks.length === 0 && ( @@ -1766,10 +1757,7 @@ export function GroupDetail({ setBlocksAllowed(filteredCoreBlocks, !coreBlocksAllAllowed)} - > + setBlocksAllowed(filteredCoreBlocks, !coreBlocksAllAllowed)}> {coreBlocksAllAllowed ? 'Deselect All' : 'Select All'} } @@ -1821,10 +1809,7 @@ export function GroupDetail({ } action={ - setBlocksAllowed(filteredToolBlocks, !toolBlocksAllAllowed)} - > + setBlocksAllowed(filteredToolBlocks, !toolBlocksAllAllowed)}> {toolBlocksAllAllowed ? 'Deselect All' : 'Select All'} } @@ -1871,7 +1856,6 @@ export function GroupDetail({ ), })) } - flush disabled={filteredPlatformFeatures.length === 0} > {platformAllVisible ? 'Deselect All' : 'Select All'} diff --git a/apps/sim/ee/custom-blocks/components/custom-block-detail.tsx b/apps/sim/ee/custom-blocks/components/custom-block-detail.tsx index d3a591c0841..d1b28825815 100644 --- a/apps/sim/ee/custom-blocks/components/custom-block-detail.tsx +++ b/apps/sim/ee/custom-blocks/components/custom-block-detail.tsx @@ -18,8 +18,8 @@ import { Switch, toast, } from '@sim/emcn' +import { ArrowLeft, ChevronDown, ImageUp as ImageIcon, X } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' -import { ArrowLeft, ChevronDown, Image as ImageIcon, X } from 'lucide-react' import { type FlattenOutputsBlockInput, type FlattenOutputsEdgeInput, diff --git a/apps/sim/ee/custom-blocks/components/custom-blocks.tsx b/apps/sim/ee/custom-blocks/components/custom-blocks.tsx index 50d0b7ebceb..a6ea613d705 100644 --- a/apps/sim/ee/custom-blocks/components/custom-blocks.tsx +++ b/apps/sim/ee/custom-blocks/components/custom-blocks.tsx @@ -2,7 +2,7 @@ import { useMemo, useState } from 'react' import { ChipTag } from '@sim/emcn' -import { ArrowRight, Plus } from 'lucide-react' +import { ArrowRight, Plus } from '@sim/emcn/icons' import { useParams } from 'next/navigation' import { useQueryState } from 'nuqs' import { canMutateWorkspaceSettingsSection } from '@/components/settings/navigation' diff --git a/apps/sim/ee/data-retention/components/data-retention-settings.tsx b/apps/sim/ee/data-retention/components/data-retention-settings.tsx index 898123fc7b4..ee56b0133c2 100644 --- a/apps/sim/ee/data-retention/components/data-retention-settings.tsx +++ b/apps/sim/ee/data-retention/components/data-retention-settings.tsx @@ -14,11 +14,10 @@ import { Search, toast, } from '@sim/emcn' -import { ArrowLeft } from '@sim/emcn/icons' +import { ArrowLeft, ArrowRight, Plus } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' import { generateId } from '@sim/utils/id' -import { ArrowRight, Plus } from 'lucide-react' import { CustomPatternsEditor } from '@/components/pii/custom-patterns-editor' import type { UpdateOrganizationDataRetentionBody } from '@/lib/api/contracts/organization' import type { RetentionOverride } from '@/lib/api/contracts/primitives' diff --git a/apps/sim/ee/sso/components/sso-settings.tsx b/apps/sim/ee/sso/components/sso-settings.tsx index 959581bd9c8..1c08beb1500 100644 --- a/apps/sim/ee/sso/components/sso-settings.tsx +++ b/apps/sim/ee/sso/components/sso-settings.tsx @@ -14,9 +14,9 @@ import { Switch, toast, } from '@sim/emcn' +import { ChevronDown, Eye, EyeOff } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { getErrorMessage } from '@sim/utils/errors' -import { ChevronDown, Eye, EyeOff } from 'lucide-react' import type { SsoRegistrationBody } from '@/lib/api/contracts/auth' import { useSession } from '@/lib/auth/auth-client' import { isEnterprise } from '@/lib/billing/plan-helpers' diff --git a/apps/sim/ee/whitelabeling/components/whitelabeling-settings.tsx b/apps/sim/ee/whitelabeling/components/whitelabeling-settings.tsx index deecd10d1ce..ab9b35eb15a 100644 --- a/apps/sim/ee/whitelabeling/components/whitelabeling-settings.tsx +++ b/apps/sim/ee/whitelabeling/components/whitelabeling-settings.tsx @@ -2,9 +2,9 @@ import { useEffect, useRef, useState } from 'react' import { Button, ChipInput, cn, Label, Loader, toast } from '@sim/emcn' +import { ImageUp as ImageIcon, X } from '@sim/emcn/icons' import { createLogger } from '@sim/logger' import { toError } from '@sim/utils/errors' -import { Image as ImageIcon, X } from 'lucide-react' import Image from 'next/image' import { isEnterprise } from '@/lib/billing/plan-helpers' import { HEX_COLOR_REGEX } from '@/lib/branding' diff --git a/apps/sim/ee/workspace-forking/components/fork-sync/fork-sync-view.tsx b/apps/sim/ee/workspace-forking/components/fork-sync/fork-sync-view.tsx index e7d7a7d6131..02e22593e73 100644 --- a/apps/sim/ee/workspace-forking/components/fork-sync/fork-sync-view.tsx +++ b/apps/sim/ee/workspace-forking/components/fork-sync/fork-sync-view.tsx @@ -12,7 +12,7 @@ import { Label, Tooltip, } from '@sim/emcn' -import { ArrowRight } from 'lucide-react' +import { ArrowRight } from '@sim/emcn/icons' import type { ForkCopyableUnmapped, ForkDependentReconfig, diff --git a/apps/sim/ee/workspace-forking/components/fork-workspace-modal/fork-workspace-modal.tsx b/apps/sim/ee/workspace-forking/components/fork-workspace-modal/fork-workspace-modal.tsx index 0623ffc0de4..0f0c10fa431 100644 --- a/apps/sim/ee/workspace-forking/components/fork-workspace-modal/fork-workspace-modal.tsx +++ b/apps/sim/ee/workspace-forking/components/fork-workspace-modal/fork-workspace-modal.tsx @@ -11,7 +11,7 @@ import { ChipModalHeader, toast, } from '@sim/emcn' -import { AlertTriangle } from 'lucide-react' +import { TriangleAlert } from '@sim/emcn/icons' import { useRouter } from 'next/navigation' import type { GetForkResourcesResponse } from '@/lib/api/contracts/workspace-fork' import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' @@ -261,7 +261,7 @@ export function ForkWorkspaceModal({ )} {hasDeselection ? (
- + Some resources are not selected — references to them in your workflows will be cleared in the fork. diff --git a/apps/sim/ee/workspace-forking/components/forks.tsx b/apps/sim/ee/workspace-forking/components/forks.tsx index 38df0a0cead..78dd557fc19 100644 --- a/apps/sim/ee/workspace-forking/components/forks.tsx +++ b/apps/sim/ee/workspace-forking/components/forks.tsx @@ -2,9 +2,8 @@ import { useState } from 'react' import { ChipConfirmModal, toast } from '@sim/emcn' -import { ArrowLeft } from '@sim/emcn/icons' +import { ArrowLeft, Plus, TriangleAlert } from '@sim/emcn/icons' import { getErrorMessage } from '@sim/utils/errors' -import { AlertTriangle, Plus } from 'lucide-react' import { useParams, useRouter } from 'next/navigation' import { useQueryState } from 'nuqs' import type { ForkLineageChildApi, ForkLineageNodeApi } from '@/lib/api/contracts/workspace-fork' @@ -550,7 +549,7 @@ export function Forks() { }} >
- + This cannot be undone — the saved mappings and sync history for this pair are deleted, and forking again creates a brand-new workspace. @@ -576,7 +575,7 @@ export function Forks() { }} >
- + Resources copied into this workspace during syncs may remain afterward — rollback restores workflows to their prior versions but does not remove copied resources. diff --git a/apps/sim/enrichments/company-domain/company-domain.ts b/apps/sim/enrichments/company-domain/company-domain.ts index 03349d07247..82ae54dca6d 100644 --- a/apps/sim/enrichments/company-domain/company-domain.ts +++ b/apps/sim/enrichments/company-domain/company-domain.ts @@ -1,4 +1,4 @@ -import { Globe } from 'lucide-react' +import { Globe } from '@sim/emcn/icons' import { normalizeDomain, str, toolProvider } from '@/enrichments/providers' import type { EnrichmentConfig } from '@/enrichments/types' diff --git a/apps/sim/enrichments/company-info/company-info.ts b/apps/sim/enrichments/company-info/company-info.ts index 407b7db95bd..43fbde12a23 100644 --- a/apps/sim/enrichments/company-info/company-info.ts +++ b/apps/sim/enrichments/company-info/company-info.ts @@ -1,5 +1,5 @@ +import { Building } from '@sim/emcn/icons' import { filterUndefined } from '@sim/utils/object' -import { Building2 } from 'lucide-react' import { normalizeDomain, str, toolProvider } from '@/enrichments/providers' import type { EnrichmentConfig } from '@/enrichments/types' @@ -15,7 +15,7 @@ export const companyInfoEnrichment: EnrichmentConfig = { id: 'company-info', name: 'Company Info', description: "Look up a company's size and description from its domain.", - icon: Building2, + icon: Building, inputs: [{ id: 'domain', name: 'Company domain', type: 'string', required: true }], outputs: [ { id: 'employeeCount', name: 'employee count', type: 'string' }, diff --git a/apps/sim/enrichments/phone-number/phone-number.ts b/apps/sim/enrichments/phone-number/phone-number.ts index 270135e20fd..c135270181a 100644 --- a/apps/sim/enrichments/phone-number/phone-number.ts +++ b/apps/sim/enrichments/phone-number/phone-number.ts @@ -1,5 +1,5 @@ +import { Phone } from '@sim/emcn/icons' import { filterUndefined } from '@sim/utils/object' -import { Phone } from 'lucide-react' import { firstNonEmpty, normalizeDomain, str, toolProvider } from '@/enrichments/providers' import type { EnrichmentConfig } from '@/enrichments/types' diff --git a/apps/sim/hooks/queries/schedules.ts b/apps/sim/hooks/queries/schedules.ts index def9e736bdd..5cb8b26e0af 100644 --- a/apps/sim/hooks/queries/schedules.ts +++ b/apps/sim/hooks/queries/schedules.ts @@ -92,9 +92,8 @@ export function useWorkspaceSchedules(workspaceId?: string, options?: { enabled? staleTime: SCHEDULE_LIST_STALE_TIME, placeholderData: keepPreviousData, // Pinned off (not inheriting the QueryClient default, which is on in the - // desktop app): a background refetch regenerates occurrence ids, which - // would close an open scheduled-task modal and drop its draft. See the - // taskById note in scheduled-tasks/hooks/use-scheduled-tasks.ts. + // desktop app): a background refetch regenerates occurrence ids, so any + // consumer holding one across a refetch would lose it mid-edit. refetchOnWindowFocus: false, }) } diff --git a/apps/sim/hooks/queries/workflow-references.ts b/apps/sim/hooks/queries/workflow-references.ts deleted file mode 100644 index 6ae68da75b4..00000000000 --- a/apps/sim/hooks/queries/workflow-references.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useQuery } from '@tanstack/react-query' -import { requestJson } from '@/lib/api/client/request' -import { - getWorkflowReferencesContract, - type WorkflowReferencesResponse, -} from '@/lib/api/contracts/workflow-references' - -/** - * Zero — the graph reflects live editor state, and no workflow-edit mutation - * invalidates this key (edits arrive over the socket, not through React Query). - * The modal mounts on demand, so every open refetches; a reopen paints the - * cached tree instantly while the background refetch reconciles it. - */ -export const WORKFLOW_REFERENCES_STALE_TIME = 0 - -export const workflowReferenceKeys = { - all: ['workflow-references'] as const, - details: () => [...workflowReferenceKeys.all, 'detail'] as const, - detail: (workflowId?: string) => [...workflowReferenceKeys.details(), workflowId ?? ''] as const, -} - -async function fetchWorkflowReferences( - workflowId: string, - signal?: AbortSignal -): Promise { - return requestJson(getWorkflowReferencesContract, { - params: { id: workflowId }, - signal, - }) -} - -export function useWorkflowReferences(workflowId?: string) { - return useQuery({ - queryKey: workflowReferenceKeys.detail(workflowId), - queryFn: ({ signal }) => fetchWorkflowReferences(workflowId as string, signal), - enabled: Boolean(workflowId), - staleTime: WORKFLOW_REFERENCES_STALE_TIME, - }) -} diff --git a/apps/sim/hooks/use-workspace-invite-policy.ts b/apps/sim/hooks/use-workspace-invite-policy.ts new file mode 100644 index 00000000000..b3ee90723b2 --- /dev/null +++ b/apps/sim/hooks/use-workspace-invite-policy.ts @@ -0,0 +1,48 @@ +'use client' + +import { useMemo } from 'react' +import { useWorkspacesQuery } from '@/hooks/queries/workspace' +import { usePermissionConfig } from '@/hooks/use-permission-config' + +export interface UseWorkspaceInvitePolicyReturn { + /** + * Why inviting is unavailable, already phrased for whoever is asking — the + * billed user is told to upgrade, everyone else to contact the owner. `null` + * when inviting is available, and also when it is blocked by a switch that has + * no viewer-facing explanation. + */ + inviteDisabledReason: string | null + /** Whether inviting is blocked at all, by any of the sources below. */ + isInvitationsDisabled: boolean +} + +/** + * The workspace's invite gate, as every entry point offering an invite or a + * teammate-management action must read it. + * + * Two independent sources say no, and both have to be consulted: the deployment + * or permission-group switch (`NEXT_PUBLIC_DISABLE_INVITATIONS`, a permission + * group's `disableInvitations`), which is silent about why, and the workspace's + * own plan policy, which supplies {@link UseWorkspaceInvitePolicyReturn.inviteDisabledReason}. + * Reading only one of them is what lets an entry point offer an invite the server + * then refuses. + * + * For callers that are already handed the workspace — the workspace switcher gets + * the whole list as a prop — deriving the same two fields inline is correct; this + * hook is for the ones that would otherwise fetch the list just to ask. + */ +export function useWorkspaceInvitePolicy(workspaceId: string): UseWorkspaceInvitePolicyReturn { + const { isInvitationsDisabled: isInvitationsDisabledByConfig } = usePermissionConfig() + const { data: workspaces } = useWorkspacesQuery() + + const inviteDisabledReason = + workspaces?.find((workspace) => workspace.id === workspaceId)?.inviteDisabledReason ?? null + + return useMemo( + () => ({ + inviteDisabledReason, + isInvitationsDisabled: isInvitationsDisabledByConfig || inviteDisabledReason !== null, + }), + [inviteDisabledReason, isInvitationsDisabledByConfig] + ) +} diff --git a/apps/sim/lib/api/contracts/workflow-references.ts b/apps/sim/lib/api/contracts/workflow-references.ts deleted file mode 100644 index c298c4f6bec..00000000000 --- a/apps/sim/lib/api/contracts/workflow-references.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { z } from 'zod' -import { nonEmptyIdSchema } from '@/lib/api/contracts/primitives' -import { defineRouteContract } from '@/lib/api/contracts/types' - -/** - * One node in a workflow reference tree. Recursive, so the Zod schema below needs - * `z.lazy` plus this explicit interface annotation — TypeScript cannot infer a - * self-referential type. Shared by the server graph builder (its return element - * type) and the client hook, keeping both off `z.output<...>`. - */ -export interface ReferenceNode { - /** Referenced workflow id. */ - id: string - /** Referenced workflow name. */ - name: string - /** - * True when this node closes a cycle already on the current path (e.g. the - * root, or `A → B → A`). Cyclic nodes carry no `children`. - */ - cycle: boolean - children: ReferenceNode[] -} - -export const referenceNodeSchema: z.ZodType = z.lazy(() => - z.object({ - id: z.string(), - name: z.string(), - cycle: z.boolean(), - children: z.array(referenceNodeSchema), - }) -) - -export const workflowReferencesParamsSchema = z.object({ - id: nonEmptyIdSchema, -}) - -export const workflowReferencesResponseSchema = z.object({ - /** Workflows that call this workflow (inbound), each recursively expanded. */ - callers: z.array(referenceNodeSchema), - /** Workflows this workflow calls (outbound), each recursively expanded. */ - callees: z.array(referenceNodeSchema), -}) - -export type WorkflowReferencesResponse = z.output - -export const getWorkflowReferencesContract = defineRouteContract({ - method: 'GET', - path: '/api/workflows/[id]/references', - params: workflowReferencesParamsSchema, - response: { - mode: 'json', - schema: workflowReferencesResponseSchema, - }, -}) diff --git a/apps/sim/lib/billing/workspace-permissions.ts b/apps/sim/lib/billing/workspace-permissions.ts index f9b7ff9daa8..41c57c574eb 100644 --- a/apps/sim/lib/billing/workspace-permissions.ts +++ b/apps/sim/lib/billing/workspace-permissions.ts @@ -1,4 +1,5 @@ import type { WorkspaceHostContext, WorkspaceUsageGate } from '@/lib/api/contracts/workspaces' +import { isBillingEnabled } from '@/lib/core/config/env-flags' export type WorkspaceUsageLimitAction = | { type: 'manage-billing'; message: null } @@ -18,6 +19,22 @@ export function canManageWorkspaceBilling( return hostContext.workspace.billedAccountUserId === viewerUserId } +/** + * Returns whether the Billing settings section is reachable for this viewer. + * + * Mirrors the section route's own gate, which sends a deployment running without + * billing back to General and 404s anyone who cannot manage the payer — on an + * organization-hosted workspace that is every member who is not an org admin. + * Menus that link to Billing drop the entry when this is false rather than + * offering a destination the server will refuse. + */ +export function canViewWorkspaceBillingSettings( + hostContext: WorkspaceHostContext, + viewerUserId?: string | null +): boolean { + return isBillingEnabled && canManageWorkspaceBilling(hostContext, viewerUserId) +} + /** * Resolves the workspace-safe action and copy for an exceeded usage gate. * Payer messages are intentionally replaced for viewers who cannot manage the diff --git a/apps/sim/lib/copilot/tools/client/store-utils.ts b/apps/sim/lib/copilot/tools/client/store-utils.ts index 92bbeb6c572..343c9e2712d 100644 --- a/apps/sim/lib/copilot/tools/client/store-utils.ts +++ b/apps/sim/lib/copilot/tools/client/store-utils.ts @@ -1,6 +1,6 @@ import type { ComponentType } from 'react' import { Loader } from '@sim/emcn' -import { FileText } from 'lucide-react' +import { FileText } from '@sim/emcn/icons' import { Read as ReadTool } from '@/lib/copilot/generated/tool-catalog-v1' import { VFS_DIR_TO_RESOURCE } from '@/lib/copilot/resources/types' import { isToolHiddenInUi } from '@/lib/copilot/tools/client/hidden-tools' diff --git a/apps/sim/lib/mothership/inbox/response.ts b/apps/sim/lib/mothership/inbox/response.ts index fbd04227574..9f76951cf8c 100644 --- a/apps/sim/lib/mothership/inbox/response.ts +++ b/apps/sim/lib/mothership/inbox/response.ts @@ -70,7 +70,7 @@ const emailStyles = { fontSize: '15px', lineHeight: '25px', color: '#1a1a1a', - fontWeight: 430, + fontWeight: 400, }, content: { margin: 0, @@ -105,7 +105,7 @@ const markdownStyles = { lineHeight: '25px', color: '#1a1a1a', fontFamily: FONT_FAMILY, - fontWeight: 430, + fontWeight: 400, }, h1: { fontWeight: 600, diff --git a/apps/sim/lib/posthog/events.ts b/apps/sim/lib/posthog/events.ts index aa520209e87..2a43855a734 100644 --- a/apps/sim/lib/posthog/events.ts +++ b/apps/sim/lib/posthog/events.ts @@ -596,11 +596,6 @@ export interface PostHogEventMap { connected_provider_count: number } - suggested_actions_shuffled: { - workspace_id: string - connected_provider_count: number - } - suggested_actions_toggled: { workspace_id: string expanded: boolean diff --git a/apps/sim/lib/workflows/references/operations.test.ts b/apps/sim/lib/workflows/references/operations.test.ts deleted file mode 100644 index 02ad33e67c9..00000000000 --- a/apps/sim/lib/workflows/references/operations.test.ts +++ /dev/null @@ -1,309 +0,0 @@ -/** - * @vitest-environment node - */ -import { describe, expect, it } from 'vitest' -import { - type CustomBlockLink, - type ReferenceBlockRow, - resolveWorkflowReferences, - type WorkflowNode, -} from '@/lib/workflows/references/operations' - -const workflows: WorkflowNode[] = [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'B' }, - { id: 'c', name: 'C' }, - { id: 'd', name: 'D' }, -] - -function workflowBlock( - parentId: string, - childId: string, - mode: 'basic' | 'manual' = 'basic', - type: 'workflow' | 'workflow_input' = 'workflow' -): ReferenceBlockRow { - return { - parentId, - type, - childFromSelector: mode === 'basic' ? childId : null, - childFromManual: mode === 'manual' ? childId : null, - canonicalModes: null, - toolInputValues: null, - } -} - -describe('resolveWorkflowReferences', () => { - it('resolves direct callers and callees', () => { - const blocks = [workflowBlock('a', 'b'), workflowBlock('a', 'c')] - const { callers, callees } = resolveWorkflowReferences('a', workflows, blocks, []) - - expect(callers).toEqual([]) - expect(callees.map((n) => n.id)).toEqual(['b', 'c']) - - const bResult = resolveWorkflowReferences('b', workflows, blocks, []) - expect(bResult.callers.map((n) => n.id)).toEqual(['a']) - expect(bResult.callees).toEqual([]) - }) - - it('resolves references made through workflow_input blocks', () => { - const blocks = [ - workflowBlock('a', 'b', 'basic', 'workflow_input'), - workflowBlock('b', 'c', 'basic', 'workflow_input'), - ] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['b']) - expect(callees[0].children.map((n) => n.id)).toEqual(['c']) - - const cResult = resolveWorkflowReferences('c', workflows, blocks, []) - expect(cResult.callers.map((n) => n.id)).toEqual(['b']) - expect(cResult.callers[0].children.map((n) => n.id)).toEqual(['a']) - }) - - it('resolves the advanced-mode manualWorkflowId value', () => { - const blocks = [workflowBlock('a', 'b', 'manual')] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['b']) - }) - - it('uses the active mode, not a retained inactive value', () => { - // Advanced mode active (canonicalModes override), but a stale basic value - // (`b`) lingers. Must resolve to the advanced value (`c`), not the stale basic. - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'a', - type: 'workflow', - childFromSelector: 'b', - childFromManual: 'c', - canonicalModes: { workflowId: 'advanced' }, - toolInputValues: null, - }, - ] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['c']) - }) - - it('marks cycles as leaves and stops recursing', () => { - const blocks = [workflowBlock('a', 'b'), workflowBlock('b', 'a')] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - - expect(callees).toHaveLength(1) - expect(callees[0]).toMatchObject({ id: 'b', cycle: false }) - expect(callees[0].children).toHaveLength(1) - expect(callees[0].children[0]).toMatchObject({ id: 'a', cycle: true, children: [] }) - }) - - it('shows a self-reference as a cycle leaf', () => { - // A → A: the reference is real and belongs in the cycle-safe viewer. - const blocks = [workflowBlock('a', 'a')] - const { callers, callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees).toEqual([{ id: 'a', name: 'A', cycle: true, children: [] }]) - expect(callers).toEqual([{ id: 'a', name: 'A', cycle: true, children: [] }]) - }) - - it('bounds converging paths (diamond) instead of re-expanding', () => { - // A → B, A → C, B → D, C → D, D → E. D reconverges; it must appear under both - // B and C but expand its subtree (E) only under the first-visited branch. - const workflowsWithE = [...workflows, { id: 'e', name: 'E' }] - const blocks = [ - workflowBlock('a', 'b'), - workflowBlock('a', 'c'), - workflowBlock('b', 'd'), - workflowBlock('c', 'd'), - workflowBlock('d', 'e'), - ] - const { callees } = resolveWorkflowReferences('a', workflowsWithE, blocks, []) - const b = callees.find((n) => n.id === 'b') - const c = callees.find((n) => n.id === 'c') - // D expands under the first-visited branch (B) and is a collapsed leaf under C. - expect(b?.children).toEqual([ - { - id: 'd', - name: 'D', - cycle: false, - children: [{ id: 'e', name: 'E', cycle: false, children: [] }], - }, - ]) - expect(c?.children).toEqual([{ id: 'd', name: 'D', cycle: false, children: [] }]) - }) - - it('truncates expansion at the depth ceiling', () => { - // A linear chain longer than MAX_REFERENCE_DEPTH (25): w0 → w1 → … → w29. - const chain = Array.from({ length: 30 }, (_, i) => ({ id: `w${i}`, name: `W${i}` })) - const blocks = Array.from({ length: 29 }, (_, i) => workflowBlock(`w${i}`, `w${i + 1}`)) - const { callees } = resolveWorkflowReferences('w0', chain, blocks, []) - let depth = 0 - let node = callees[0] - while (node) { - depth += 1 - node = node.children[0] - } - expect(depth).toBe(25) - }) - - it('re-expands a depth-truncated node when a shallower path reaches it', () => { - // Root fans out to a 25-deep chain (visited first by name sort: "A…") whose - // tail X gets truncated at the ceiling, and a direct edge (via "Z") to X. - // The shallow path must still show X's child Y instead of a collapsed leaf. - const nodes = [ - { id: 'root', name: 'Root' }, - ...Array.from({ length: 24 }, (_, i) => ({ - id: `a${i}`, - name: `A${String(i).padStart(2, '0')}`, - })), - { id: 'x', name: 'X' }, - { id: 'y', name: 'Y' }, - { id: 'z', name: 'Z' }, - ] - const blocks = [ - workflowBlock('root', 'a0'), - ...Array.from({ length: 23 }, (_, i) => workflowBlock(`a${i}`, `a${i + 1}`)), - workflowBlock('a23', 'x'), - workflowBlock('root', 'z'), - workflowBlock('z', 'x'), - workflowBlock('x', 'y'), - ] - const { callees } = resolveWorkflowReferences('root', nodes, blocks, []) - const z = callees.find((n) => n.id === 'z') - const xUnderZ = z?.children.find((n) => n.id === 'x') - expect(xUnderZ?.children.map((n) => n.id)).toEqual(['y']) - }) - - it('drops dangling / out-of-workspace child ids', () => { - const blocks = [workflowBlock('a', 'missing')] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees).toEqual([]) - }) - - it('resolves references made through custom blocks', () => { - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'd', - type: 'custom_block_x', - childFromSelector: null, - childFromManual: null, - canonicalModes: null, - toolInputValues: null, - }, - ] - const customBlocks: CustomBlockLink[] = [{ type: 'custom_block_x', workflowId: 'c' }] - - const cResult = resolveWorkflowReferences('c', workflows, blocks, customBlocks) - expect(cResult.callers.map((n) => n.id)).toEqual(['d']) - - const dResult = resolveWorkflowReferences('d', workflows, blocks, customBlocks) - expect(dResult.callees.map((n) => n.id)).toEqual(['c']) - }) - - it('ignores custom blocks with no bound source in scope', () => { - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'd', - type: 'custom_block_unknown', - childFromSelector: null, - childFromManual: null, - canonicalModes: null, - toolInputValues: null, - }, - ] - const { callees } = resolveWorkflowReferences('d', workflows, blocks, []) - expect(callees).toEqual([]) - }) - - it('returns empty trees when the workflow is not a workspace node', () => { - const blocks = [workflowBlock('a', 'b')] - const result = resolveWorkflowReferences('unknown', workflows, blocks, []) - expect(result).toEqual({ callers: [], callees: [] }) - }) - - it('sorts children by name', () => { - // Names: B, C — insert in reverse to prove sorting. - const blocks = [workflowBlock('a', 'c'), workflowBlock('a', 'b')] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.name)).toEqual(['B', 'C']) - }) - - it('resolves workflow tools inside tool-input sub-blocks', () => { - // Agent block on A carrying a workflow_input tool that calls B; a non-workflow - // tool and a malformed entry must be ignored. - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'a', - type: 'agent', - childFromSelector: null, - childFromManual: null, - canonicalModes: null, - toolInputValues: [ - [ - { type: 'workflow_input', params: { workflowId: 'b' } }, - { type: 'function', params: {} }, - { type: 'workflow_input' }, - ], - ], - }, - ] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['b']) - - const bResult = resolveWorkflowReferences('b', workflows, blocks, []) - expect(bResult.callers.map((n) => n.id)).toEqual(['a']) - }) - - it('resolves a workflow tool to its active advanced-mode value', () => { - // Tool 0 is toggled to advanced via the index-scoped canonicalModes key; the - // stale basic selector (`b`) must not mask the live manual value (`c`). - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'a', - type: 'agent', - childFromSelector: null, - childFromManual: null, - canonicalModes: { '0:workflowId': 'advanced' }, - toolInputValues: [ - [{ type: 'workflow_input', params: { workflowId: 'b', manualWorkflowId: 'c' } }], - ], - }, - ] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['c']) - }) - - it('resolves legacy workflow-typed tools and isolates index-scoped modes per tool', () => { - // Tool 0 is a legacy `workflow`-typed entry (still rendered/executed by the - // editor); tool 1 is advanced-mode via its own index-scoped key. Tool 0 must - // stay basic (`b`) — tool 1's override must not bleed into it. - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'a', - type: 'agent', - childFromSelector: null, - childFromManual: null, - canonicalModes: { '1:workflowId': 'advanced' }, - toolInputValues: [ - [ - { type: 'workflow', params: { workflowId: 'b' } }, - { type: 'workflow_input', params: { workflowId: 'c', manualWorkflowId: 'd' } }, - ], - ], - }, - ] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['b', 'd']) - }) - - it('resolves workflow tools from a JSON-stringified tool-input value', () => { - const blocks: ReferenceBlockRow[] = [ - { - parentId: 'a', - type: 'agent', - childFromSelector: null, - childFromManual: null, - canonicalModes: null, - toolInputValues: [ - JSON.stringify([{ type: 'workflow_input', params: { workflowId: 'c' } }]), - ], - }, - ] - const { callees } = resolveWorkflowReferences('a', workflows, blocks, []) - expect(callees.map((n) => n.id)).toEqual(['c']) - }) -}) diff --git a/apps/sim/lib/workflows/references/operations.ts b/apps/sim/lib/workflows/references/operations.ts deleted file mode 100644 index 768539b286d..00000000000 --- a/apps/sim/lib/workflows/references/operations.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { db } from '@sim/db' -import { workflow, workflowBlocks } from '@sim/db/schema' -import { and, eq, inArray, isNull, or, sql } from 'drizzle-orm' -import type { ReferenceNode } from '@/lib/api/contracts/workflow-references' -import { MAX_CALL_CHAIN_DEPTH } from '@/lib/execution/call-chain' -import { getCustomBlockRowsForWorkspace } from '@/lib/workflows/custom-blocks/operations' -import { coerceObjectArray, isRecord } from '@/lib/workflows/persistence/remap-internal-ids' -import { - type CanonicalGroup, - type CanonicalModeOverrides, - resolveActiveCanonicalValue, - scopeCanonicalModesForTool, -} from '@/lib/workflows/subblocks/visibility' -import { CUSTOM_BLOCK_TYPE_PREFIX } from '@/blocks/custom/build-config' -import { BlockType, isWorkflowBlockType } from '@/executor/constants' - -/** - * Depth ceiling for a reference tree — the runtime call-chain bound; a display - * tree never needs to show more than the executor allows. The per-path visited - * set is the real cycle guard; this is a belt-and-suspenders bound on - * pathological graphs. - */ -const MAX_REFERENCE_DEPTH = MAX_CALL_CHAIN_DEPTH - -/** - * The `workflowId` canonical pair on a workflow / workflow_input block: the basic - * `workflowId` selector and the advanced `manualWorkflowId` input. Used with - * {@link resolveActiveCanonicalValue} so the reference resolves to the value of the - * block's ACTIVE mode — never a dormant mode's retained (stale) value. - * (`remapWorkflowReferencesInSubBlocks` in - * `@/lib/workflows/persistence/remap-internal-ids` builds the same pair from - * positional keys — keep the two in sync if the pair ever changes.) - */ -const WORKFLOW_ID_CANONICAL_GROUP: CanonicalGroup = { - canonicalId: 'workflowId', - basicId: 'workflowId', - advancedIds: ['manualWorkflowId'], -} - -/** `custom_block_` with LIKE wildcards (`_`) escaped, for the SQL prefix match. */ -const CUSTOM_BLOCK_LIKE_PREFIX = CUSTOM_BLOCK_TYPE_PREFIX.replace(/[\\%_]/g, '\\$&') - -/** A workspace-local, non-archived workflow node. */ -export interface WorkflowNode { - id: string - name: string -} - -/** A placed block that may reference another workflow (raw, pre-resolution). */ -export interface ReferenceBlockRow { - parentId: string - type: string - /** `workflowId` sub-block value (basic mode), if a workflow block. */ - childFromSelector: string | null - /** `manualWorkflowId` sub-block value (advanced mode), if a workflow block. */ - childFromManual: string | null - /** - * The block's `data.canonicalModes` override (basic/advanced per canonical id), - * used to pick the active `workflowId` value. Absent for most blocks. - */ - canonicalModes: CanonicalModeOverrides | null - /** - * Aggregated `tool-input` sub-block values on this block (agent-style tool - * lists). Each entry is one sub-block's raw value — an array of tool objects, - * or that array JSON-stringified. `workflow_input` tools carry the callee id - * in `params.workflowId` (basic) or `params.manualWorkflowId` (advanced). - * Null when the block has no tool-input sub-blocks. - */ - toolInputValues: unknown[] | null -} - -/** A custom-block type slug bound to its source workflow. */ -export interface CustomBlockLink { - type: string - workflowId: string -} - -/** - * The workspace-wide reference graph derived from live editor state: every - * workflow node's name, plus forward (callee) and reverse (caller) adjacency. - */ -interface ReferenceGraph { - nameById: Map - forward: Map> - reverse: Map> -} - -/** - * Callee workflow ids referenced by a block's tool-input values: workflow tools - * (`workflow_input`, plus legacy stored entries typed `workflow`) resolved to - * their ACTIVE canonical member — the basic `params.workflowId` selector or the - * advanced `params.manualWorkflowId` input, per the tool's index-scoped - * `canonicalModes` override ({@link scopeCanonicalModesForTool}) — mirroring how - * execution picks the live value. - */ -function toolInputCallees( - toolInputValues: unknown[] | null, - canonicalModes: CanonicalModeOverrides | null -): string[] { - if (!toolInputValues) return [] - const callees: string[] = [] - for (const value of toolInputValues) { - const { array } = coerceObjectArray(value) - if (!array) continue - array.forEach((tool, toolIndex) => { - if ( - !isRecord(tool) || - typeof tool.type !== 'string' || - !isWorkflowBlockType(tool.type) || - !isRecord(tool.params) - ) { - return - } - const scoped = scopeCanonicalModesForTool(canonicalModes ?? undefined, toolIndex, tool.type) - const active = resolveActiveCanonicalValue( - WORKFLOW_ID_CANONICAL_GROUP, - { - workflowId: typeof tool.params.workflowId === 'string' ? tool.params.workflowId : null, - manualWorkflowId: - typeof tool.params.manualWorkflowId === 'string' ? tool.params.manualWorkflowId : null, - }, - scoped - ) - if (typeof active === 'string' && active) callees.push(active) - }) - } - return callees -} - -/** - * Build the directed reference graph from raw workspace rows. Pure (no I/O) so it - * can be unit-tested directly. Resolves three call-edge shapes: - * - direct **workflow blocks** (`workflow` and `workflow_input`), whose child id - * is the `workflowId` (basic) or `manualWorkflowId` (advanced) sub-block value; - * - **custom blocks** (`custom_block_`), whose type slug maps to a bound - * source workflow via `customBlocks`; and - * - **workflow tools** — `workflow_input` entries inside a block's `tool-input` - * sub-blocks (an agent invoking another workflow as a tool). - * - * Non-call reference shapes (the logs block's `workflowSelector` monitor list and - * the workspace-event trigger's `workflowIds`; see - * `remapWorkflowReferencesInSubBlocks`) are deliberately excluded — the viewer - * shows call relationships. - * - * The workflow-block child is the value of the block's ACTIVE mode - * ({@link resolveActiveCanonicalValue}), so a dormant basic/advanced value can't - * mask the live one. Edges are scoped to workspace-local, non-archived workflows; - * empty values and ids outside `workflows` are dropped. A workflow that calls - * itself is kept — the tree builder renders it as a `cycle` leaf. - */ -function buildReferenceGraph( - workflows: WorkflowNode[], - blocks: ReferenceBlockRow[], - customBlocks: CustomBlockLink[] -): ReferenceGraph { - const nameById = new Map() - for (const node of workflows) nameById.set(node.id, node.name) - - const sourceByCustomType = new Map() - for (const link of customBlocks) sourceByCustomType.set(link.type, link.workflowId) - - const forward = new Map>() - const reverse = new Map>() - - const addEdge = (parentId: string, childId: string) => { - if (!childId) return - if (!nameById.has(parentId) || !nameById.has(childId)) return - let callees = forward.get(parentId) - if (!callees) forward.set(parentId, (callees = new Set())) - callees.add(childId) - let callers = reverse.get(childId) - if (!callers) reverse.set(childId, (callers = new Set())) - callers.add(parentId) - } - - for (const block of blocks) { - if (isWorkflowBlockType(block.type)) { - const active = resolveActiveCanonicalValue( - WORKFLOW_ID_CANONICAL_GROUP, - { workflowId: block.childFromSelector, manualWorkflowId: block.childFromManual }, - block.canonicalModes ?? undefined - ) - if (typeof active === 'string' && active) addEdge(block.parentId, active) - } else { - const sourceId = sourceByCustomType.get(block.type) - if (sourceId) addEdge(block.parentId, sourceId) - } - for (const calleeId of toolInputCallees(block.toolInputValues, block.canonicalModes)) { - addEdge(block.parentId, calleeId) - } - } - - return { nameById, forward, reverse } -} - -/** - * Expand a direction of the graph into a tree rooted at `rootId`. `adjacency` is - * either the forward (callees) or reverse (callers) map. - * - * A node already on the current DFS path is emitted as a `cycle: true` leaf and - * not re-expanded, so `A → B → A` (and a self-call `A → A`) terminates. A node - * already fully expanded elsewhere in this tree (reachable via another acyclic - * path — a diamond) is emitted once more as a plain leaf without re-expanding its - * subtree: the edge stays visible, but a densely reconverging graph can't blow up - * exponentially. Children are sorted by name for stable rendering. - */ -function buildTree( - rootId: string, - adjacency: Map>, - nameById: Map -): ReferenceNode[] { - const path = new Set([rootId]) - const expanded = new Set() - const nameOf = (id: string) => nameById.get(id) as string - - const expand = (id: string, depth: number): ReferenceNode[] => { - if (depth >= MAX_REFERENCE_DEPTH) return [] - const neighbors = adjacency.get(id) - if (!neighbors || neighbors.size === 0) return [] - - const sorted = [...neighbors].sort((a, b) => nameOf(a).localeCompare(nameOf(b))) - - const nodes: ReferenceNode[] = [] - for (const childId of sorted) { - const name = nameOf(childId) - if (path.has(childId)) { - nodes.push({ id: childId, name, cycle: true, children: [] }) - continue - } - if (expanded.has(childId)) { - nodes.push({ id: childId, name, cycle: false, children: [] }) - continue - } - expanded.add(childId) - path.add(childId) - nodes.push({ id: childId, name, cycle: false, children: expand(childId, depth + 1) }) - path.delete(childId) - // A depth-capped expansion is incomplete — allow a shallower path to retry - // it in full instead of collapsing to a leaf. Each retry starts strictly - // shallower, so this stays bounded. - if (depth + 1 >= MAX_REFERENCE_DEPTH) expanded.delete(childId) - } - return nodes - } - - return expand(rootId, 0) -} - -/** - * Resolve the reference trees for one workflow from raw workspace rows. Pure so it - * can be unit-tested without a database. Returns empty arrays when the workflow is - * not a workspace-local node or has no references. - */ -export function resolveWorkflowReferences( - workflowId: string, - workflows: WorkflowNode[], - blocks: ReferenceBlockRow[], - customBlocks: CustomBlockLink[] -): { callers: ReferenceNode[]; callees: ReferenceNode[] } { - const { nameById, forward, reverse } = buildReferenceGraph(workflows, blocks, customBlocks) - - if (!nameById.has(workflowId)) { - return { callers: [], callees: [] } - } - - return { - callers: buildTree(workflowId, reverse, nameById), - callees: buildTree(workflowId, forward, nameById), - } -} - -/** - * Resolve the reference trees for one workflow: `callers` (workflows that call - * it, inbound) and `callees` (workflows it calls, outbound), read from the live - * `workflowBlocks` (draft) table — the state the sidebar and editor show. The - * root itself is not a node; each array holds its direct references, recursively - * expanded. - */ -export async function getWorkflowReferences( - workspaceId: string, - workflowId: string -): Promise<{ callers: ReferenceNode[]; callees: ReferenceNode[] }> { - const hasToolInput = sql`EXISTS ( - SELECT 1 FROM jsonb_each(${workflowBlocks.subBlocks}) AS kv - WHERE kv.value ->> 'type' = 'tool-input' - )` - - const [workflowRows, blockRows, customBlockRows] = await Promise.all([ - db - .select({ id: workflow.id, name: workflow.name }) - .from(workflow) - .where(and(eq(workflow.workspaceId, workspaceId), isNull(workflow.archivedAt))), - db - .select({ - parentId: workflowBlocks.workflowId, - type: workflowBlocks.type, - childFromSelector: sql< - string | null - >`${workflowBlocks.subBlocks} -> 'workflowId' ->> 'value'`, - childFromManual: sql< - string | null - >`${workflowBlocks.subBlocks} -> 'manualWorkflowId' ->> 'value'`, - canonicalModes: sql`${workflowBlocks.data} -> 'canonicalModes'`, - toolInputValues: sql`( - SELECT jsonb_agg(kv.value -> 'value') - FROM jsonb_each(${workflowBlocks.subBlocks}) AS kv - WHERE kv.value ->> 'type' = 'tool-input' - )`, - }) - .from(workflowBlocks) - .innerJoin(workflow, eq(workflow.id, workflowBlocks.workflowId)) - .where( - and( - eq(workflow.workspaceId, workspaceId), - isNull(workflow.archivedAt), - or( - inArray(workflowBlocks.type, [BlockType.WORKFLOW, BlockType.WORKFLOW_INPUT]), - sql`${workflowBlocks.type} LIKE ${`${CUSTOM_BLOCK_LIKE_PREFIX}%`} ESCAPE '\\'`, - hasToolInput - ) - ) - ), - getCustomBlockRowsForWorkspace(workspaceId), - ]) - - return resolveWorkflowReferences(workflowId, workflowRows, blockRows, customBlockRows) -} diff --git a/apps/sim/package.json b/apps/sim/package.json index 2fc8a509e82..e1d3ff46b4b 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -180,7 +180,6 @@ "js-yaml": "4.3.0", "jszip": "3.10.1", "lru-cache": "11.3.6", - "lucide-react": "^0.511.0", "mammoth": "^1.9.0", "mermaid": "11.15.0", "micromatch": "4.0.8", diff --git a/apps/sim/stores/constants.ts b/apps/sim/stores/constants.ts index 1046d3d2315..16785e60598 100644 --- a/apps/sim/stores/constants.ts +++ b/apps/sim/stores/constants.ts @@ -23,8 +23,8 @@ export const CONTENT_WINDOW_GAP = 8 /** Sidebar width constraints */ export const SIDEBAR_WIDTH = { - DEFAULT: 248, - MIN: 248, + DEFAULT: 238, + MIN: 238, /** Width when sidebar is collapsed to icon-only mode */ COLLAPSED: 51, /** Maximum is 30% of viewport, enforced dynamically */ diff --git a/apps/sim/stores/modals/search/store.ts b/apps/sim/stores/modals/search/store.ts index 7084b591044..f21d516ddc4 100644 --- a/apps/sim/stores/modals/search/store.ts +++ b/apps/sim/stores/modals/search/store.ts @@ -1,4 +1,4 @@ -import { RepeatIcon, SplitIcon } from 'lucide-react' +import { Repeat, Split } from '@sim/emcn/icons' import { create } from 'zustand' import { devtools } from 'zustand/middleware' import { getToolOperationsIndex } from '@/lib/search/tool-operations' @@ -128,14 +128,14 @@ export const useSearchModalStore = create()( { id: 'loop', name: 'Loop', - icon: RepeatIcon, + icon: Repeat, bgColor: '#2FB3FF', type: 'loop', }, { id: 'parallel', name: 'Parallel', - icon: SplitIcon, + icon: Split, bgColor: '#FEE12B', type: 'parallel', }, diff --git a/apps/sim/tailwind.config.ts b/apps/sim/tailwind.config.ts index 796ec178263..ed0a228090d 100644 --- a/apps/sim/tailwind.config.ts +++ b/apps/sim/tailwind.config.ts @@ -134,11 +134,6 @@ export default { 950: '#0a0a0a', }, }, - fontWeight: { - base: 'var(--font-weight-base)', - medium: 'var(--font-weight-medium)', - semibold: 'var(--font-weight-semibold)', - }, borderRadius: { xs: '2px', sm: 'calc(var(--radius) - 4px)', @@ -152,8 +147,18 @@ export default { kbd: 'var(--shadow-kbd)', 'kbd-sm': 'var(--shadow-kbd-sm)', card: 'var(--shadow-card)', + ambient: 'var(--shadow-ambient)', }, dropShadow: {}, + maxWidth: { + /** + * The home/chat reading column. The heading, input, suggested actions, + * transcript, and its skeleton must all share one value or the footer + * input visibly misaligns with the messages above it — so they read this + * key rather than repeating a literal. + */ + chat: '44rem', + }, transitionProperty: { width: 'width', left: 'left', diff --git a/bun.lock b/bun.lock index 1842309a2e6..c2a5be50add 100644 --- a/bun.lock +++ b/bun.lock @@ -75,7 +75,6 @@ "fumadocs-mdx": "14.3.2", "fumadocs-openapi": "10.8.1", "fumadocs-ui": "16.8.5", - "lucide-react": "^0.511.0", "next": "16.2.12", "next-themes": "^0.4.6", "react": "19.2.4", @@ -284,7 +283,6 @@ "js-yaml": "4.3.0", "jszip": "3.10.1", "lru-cache": "11.3.6", - "lucide-react": "^0.511.0", "mammoth": "^1.9.0", "mermaid": "11.15.0", "micromatch": "4.0.8", @@ -482,7 +480,6 @@ "class-variance-authority": "^0.7.1", "framer-motion": "^12.5.0", "input-otp": "^1.4.2", - "lucide-react": "^0.511.0", "next": "16.2.12", "prismjs": "^1.30.0", "react": "19.2.4", @@ -507,7 +504,6 @@ "class-variance-authority": "^0.7.1", "framer-motion": "^12.5.0", "input-otp": "^1.4.2", - "lucide-react": ">=0.479.0", "next": ">=15", "prismjs": "^1.30.0", "react": "^19", @@ -654,7 +650,6 @@ "@sim/tsconfig": "workspace:*", "@sim/utils": "workspace:*", "@types/react": "^19", - "lucide-react": "^0.511.0", "react": "19.2.4", "reactflow": "^11.11.4", "remark-breaks": "^4.0.0", @@ -664,7 +659,6 @@ "peerDependencies": { "@sim/emcn": "workspace:*", "@sim/utils": "workspace:*", - "lucide-react": ">=0.479.0", "react": "^19", "reactflow": "^11.11.4", "remark-breaks": "^4.0.0", diff --git a/packages/emcn/package.json b/packages/emcn/package.json index 6089d9afbe7..55455c98a66 100644 --- a/packages/emcn/package.json +++ b/packages/emcn/package.json @@ -56,7 +56,6 @@ "class-variance-authority": "^0.7.1", "framer-motion": "^12.5.0", "input-otp": "^1.4.2", - "lucide-react": ">=0.479.0", "next": ">=15", "prismjs": "^1.30.0", "react": "^19", @@ -83,7 +82,6 @@ "class-variance-authority": "^0.7.1", "framer-motion": "^12.5.0", "input-otp": "^1.4.2", - "lucide-react": "^0.511.0", "next": "16.2.12", "prismjs": "^1.30.0", "react": "19.2.4", diff --git a/packages/emcn/src/components/button/button.tsx b/packages/emcn/src/components/button/button.tsx index 80eebd30722..e1916bda1e4 100644 --- a/packages/emcn/src/components/button/button.tsx +++ b/packages/emcn/src/components/button/button.tsx @@ -2,6 +2,24 @@ import { type ButtonHTMLAttributes, forwardRef } from 'react' import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '../../lib/cn' +/** + * `size='icon'` is the square 20px icon-only button — a chip field's trailing + * affordance, a toast dismiss, a section-header action. It drops the text padding + * the `sm`/`md` sizes carry and tightens the radius to `rounded-sm` (4px), which + * reads correctly at this size where the base 5px does not. The box is deliberately + * larger than every glyph it holds; that margin IS the button's padding, since the + * glyph is sized at the call site rather than here. + * + * Glyphs also draw one step thinner than the 1.55 the icon set ships, so a lone + * icon reads as a secondary affordance rather than a piece of UI text. CSS wins + * over the SVG's own `stroke-width` attribute, so this reaches every stroked icon + * without touching the icon components — including the few that ship at 2. + * + * Compose it with `quiet` (the usual choice) or `ghost` (where the surrounding + * surface owns the hover) — those pairings also pick up the muted icon color. + * + * @example + */ const buttonVariants = cva( 'inline-flex items-center justify-center font-medium transition-colors disabled:pointer-events-none disabled:opacity-70 outline-none focus:outline-none focus-visible:outline-none rounded-[5px]', { @@ -30,8 +48,19 @@ const buttonVariants = cva( size: { sm: 'px-1.5 py-1 text-[length:11px]', md: 'px-2 py-1.5 text-[length:12px]', + icon: 'size-[20px] rounded-sm p-0 [&_svg]:[stroke-width:1.25]', }, }, + compoundVariants: [ + /** + * A lone glyph is icon content, not text, so the neutral icon buttons paint + * with `--text-icon-muted` rather than the variant's text color. Scoped to + * the neutral variants: the filled ones (`primary`, `destructive`, …) carry + * inverse text that must keep winning over their own surface. + */ + { size: 'icon', variant: 'quiet', className: 'text-[var(--text-icon-muted)]' }, + { size: 'icon', variant: 'ghost', className: 'text-[var(--text-icon-muted)]' }, + ], defaultVariants: { variant: 'default', size: 'md', diff --git a/packages/emcn/src/components/calendar/calendar-day-cell.tsx b/packages/emcn/src/components/calendar/calendar-day-cell.tsx index 33f070af65c..e3c49796c15 100644 --- a/packages/emcn/src/components/calendar/calendar-day-cell.tsx +++ b/packages/emcn/src/components/calendar/calendar-day-cell.tsx @@ -45,7 +45,6 @@ export const CalendarDayCell = forwardRef Time - +
)} @@ -547,9 +539,9 @@ function RangeCalendarView({ {showTime && (
- + to - +
)} diff --git a/packages/emcn/src/components/checkbox/checkbox.tsx b/packages/emcn/src/components/checkbox/checkbox.tsx index 92c5dee6f90..ba721515e96 100644 --- a/packages/emcn/src/components/checkbox/checkbox.tsx +++ b/packages/emcn/src/components/checkbox/checkbox.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import * as CheckboxPrimitive from '@radix-ui/react-checkbox' import { cva, type VariantProps } from 'class-variance-authority' -import { Check, Minus } from 'lucide-react' +import { Check, Minus } from '../../icons' import { cn } from '../../lib/cn' /** diff --git a/packages/emcn/src/components/chip-copy-input/chip-copy-input.tsx b/packages/emcn/src/components/chip-copy-input/chip-copy-input.tsx index cee80bf7e5c..75111802089 100644 --- a/packages/emcn/src/components/chip-copy-input/chip-copy-input.tsx +++ b/packages/emcn/src/components/chip-copy-input/chip-copy-input.tsx @@ -52,7 +52,7 @@ export const ChipCopyInput = React.forwardRef copy(value)} aria-label={copyLabel} > diff --git a/packages/emcn/src/components/chip-date-picker/chip-date-picker.tsx b/packages/emcn/src/components/chip-date-picker/chip-date-picker.tsx index 42d83aa5452..b131ccee387 100644 --- a/packages/emcn/src/components/chip-date-picker/chip-date-picker.tsx +++ b/packages/emcn/src/components/chip-date-picker/chip-date-picker.tsx @@ -29,8 +29,6 @@ interface ChipDatePickerBaseProps { disabled?: boolean /** Stretch the trigger to fill its container (mirrors `Chip`'s `fullWidth`). */ fullWidth?: boolean - /** Removes the default `mx-0.5` cluster margin (mirrors `Chip`'s `flush`). */ - flush?: boolean /** Forwarded class for the trigger button. */ className?: string } @@ -87,7 +85,6 @@ const ChipDatePicker = forwardRef( align = 'start', disabled, fullWidth, - flush, className, } = props @@ -108,8 +105,8 @@ const ChipDatePicker = forwardRef( disabled={disabled} className={cn( variant === 'ghost' - ? chipVariants({ fullWidth, flush }) - : cn(chipVariants({ variant: 'filled', fullWidth, flush }), TRIGGER_BORDER_CLASS), + ? chipVariants({ fullWidth }) + : cn(chipVariants({ variant: 'filled', fullWidth }), TRIGGER_BORDER_CLASS), className )} > diff --git a/packages/emcn/src/components/chip-dropdown/chip-dropdown.tsx b/packages/emcn/src/components/chip-dropdown/chip-dropdown.tsx index 67c4b7a8b02..6173e1ed96d 100644 --- a/packages/emcn/src/components/chip-dropdown/chip-dropdown.tsx +++ b/packages/emcn/src/components/chip-dropdown/chip-dropdown.tsx @@ -164,7 +164,6 @@ type ChipDropdownProps = ChipDropdownSingleProps | ChipDropdownMultiProps * searchable * searchPlaceholder='Search members...' * fullWidth - * flush * /> */ const ChipDropdown = forwardRef( @@ -181,7 +180,6 @@ const ChipDropdown = forwardRef( variant = 'filled', active, fullWidth, - flush, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, id, @@ -308,7 +306,7 @@ const ChipDropdown = forwardRef( aria-label={ariaLabel} aria-labelledby={ariaLabelledBy} className={cn( - chipVariants({ variant, active, fullWidth, flush }), + chipVariants({ variant, active, fullWidth }), hasTriggerBorder && TRIGGER_BORDER_CLASS, className )} diff --git a/packages/emcn/src/components/chip-input/chip-input.tsx b/packages/emcn/src/components/chip-input/chip-input.tsx index 9cb72304fe3..1266a9aa5b9 100644 --- a/packages/emcn/src/components/chip-input/chip-input.tsx +++ b/packages/emcn/src/components/chip-input/chip-input.tsx @@ -32,7 +32,7 @@ import { chipFieldSurfaceClass, chipFieldTextClass } from '../chip/chip-chrome' type ChipInputIcon = React.ComponentType<{ className?: string }> export interface ChipInputProps extends Omit, 'size'> { - /** Leading icon component (e.g. lucide `Search`). Rendered at 14px in `--text-icon`, with the chip's 1.5 gap. */ + /** Leading icon component (e.g. `Search` from `@sim/emcn/icons`). Rendered at 14px in `--text-icon`, with the chip's 1.5 gap. */ icon?: ChipInputIcon /** Trailing content rendered after the input (e.g. reveal / copy buttons). */ endAdornment?: React.ReactNode diff --git a/packages/emcn/src/components/chip-modal/chip-modal.tsx b/packages/emcn/src/components/chip-modal/chip-modal.tsx index 586c7e1fbfb..2a00aaf5468 100644 --- a/packages/emcn/src/components/chip-modal/chip-modal.tsx +++ b/packages/emcn/src/components/chip-modal/chip-modal.tsx @@ -39,8 +39,7 @@ 'use client' import * as React from 'react' -import { X } from 'lucide-react' -import { Loader } from '../../icons' +import { Loader, X } from '../../icons' import { cn } from '../../lib/cn' import { Button } from '../button/button' import { Chip, type ChipProps } from '../chip/chip' @@ -987,10 +986,10 @@ export interface ChipModalFooterAction { * Escape hatch for the left-docked footer cluster: renders the given node in * place of a declarative action Chip. Reserve it for chip-chrome controls * (`ChipDatePicker`, `ChipTimePicker`, `ChipDropdown`, ...) so the footer - * stays visually canonical — pass `flush` to the control so it sits on the - * cluster's `gap-2` rhythm like the footer's own Chips. The primary action - * stays declarative by design; only `secondaryActions` accepts custom - * controls. + * stays visually canonical — the cluster's `gap-2` alone sets the rhythm, as + * it does for the footer's own Chips, so the control must carry no outer + * margin. The primary action stays declarative by design; only + * `secondaryActions` accepts custom controls. */ export interface ChipModalFooterCustomAction { /** Chip-chrome control rendered verbatim in the slot. */ @@ -1089,7 +1088,7 @@ function ChipModalFooterShell({ function renderFooterSlotAction(action: ChipModalFooterSlotAction): React.ReactNode { if ('custom' in action) return action.custom return ( - + {action.label} ) @@ -1148,7 +1147,6 @@ function ChipModalFooter({ const primaryChip = ( {hideCancel ? null : ( - + Cancel )} @@ -1407,12 +1405,11 @@ function ChipConfirmModal({ {children} - + {dismissLabel} diff --git a/packages/emcn/src/components/chip-select/chip-select.tsx b/packages/emcn/src/components/chip-select/chip-select.tsx index 05565b47034..fd528fe6913 100644 --- a/packages/emcn/src/components/chip-select/chip-select.tsx +++ b/packages/emcn/src/components/chip-select/chip-select.tsx @@ -227,7 +227,7 @@ export function ChipSelect({ disabled={disabled} aria-label={ariaLabel} className={cn( - chipVariants({ variant: 'filled', flush: true, fullWidth }), + chipVariants({ variant: 'filled', fullWidth }), TRIGGER_BORDER_CLASS, fullWidth ? 'w-full justify-between' : 'w-fit max-w-[240px]', className diff --git a/packages/emcn/src/components/chip-switch/chip-switch.tsx b/packages/emcn/src/components/chip-switch/chip-switch.tsx index cf0cf535de2..dcf287749b2 100644 --- a/packages/emcn/src/components/chip-switch/chip-switch.tsx +++ b/packages/emcn/src/components/chip-switch/chip-switch.tsx @@ -80,7 +80,6 @@ export function ChipSwitch({ className={cn( chipVariants({ variant: isActive ? 'border-shadow' : 'default', - flush: true, }), 'justify-center', isActive diff --git a/packages/emcn/src/components/chip-time-picker/chip-time-picker.tsx b/packages/emcn/src/components/chip-time-picker/chip-time-picker.tsx index 83d2d25964a..6bebb61d419 100644 --- a/packages/emcn/src/components/chip-time-picker/chip-time-picker.tsx +++ b/packages/emcn/src/components/chip-time-picker/chip-time-picker.tsx @@ -52,8 +52,6 @@ export interface ChipTimePickerProps { disabled?: boolean /** Stretch the field to fill its container (mirrors `Chip`'s `fullWidth`). */ fullWidth?: boolean - /** Removes the default `mx-0.5` cluster margin (mirrors `Chip`'s `flush`). */ - flush?: boolean /** Layout/sizing only — width overrides, margins. The chrome is owned by the chip field. */ className?: string } @@ -66,11 +64,11 @@ export interface ChipTimePickerProps { * doesn't parse reverts to the last committed time. * * @example - * + * */ const ChipTimePicker = React.forwardRef( function ChipTimePicker( - { value, onChange, placeholder = '10:00 AM', disabled, fullWidth, flush, className }, + { value, onChange, placeholder = '10:00 AM', disabled, fullWidth, className }, ref ) { const [text, setText] = React.useState(() => formatTimeLabel(value)) @@ -124,7 +122,7 @@ const ChipTimePicker = React.forwardRef( aria-label='Time' autoComplete='off' spellCheck={false} - className={cn(fullWidth ? 'w-full' : 'w-[88px]', flush ? 'mx-0' : 'mx-0.5', className)} + className={cn(fullWidth ? 'w-full' : 'w-[88px]', className)} /> ) } diff --git a/packages/emcn/src/components/chip/chip.tsx b/packages/emcn/src/components/chip/chip.tsx index fb868dd8b18..fb87f1684e9 100644 --- a/packages/emcn/src/components/chip/chip.tsx +++ b/packages/emcn/src/components/chip/chip.tsx @@ -36,8 +36,12 @@ import { * `border` (the `border-shadow` shadow ring on a transparent surface — an outline drawn purely via box-shadow, * no CSS border, no fill). * `active` renders the default/filled chip in its selected state — `--surface-active` at rest, one surface darker - * (`--surface-6`) on hover. `fullWidth` swaps `inline-flex` for block-level `flex`. `flush` removes the default - * `mx-0.5` cluster margin — use when a single chip sits in its own layout slot (grid/table cell). + * (`--surface-6`) on hover. `fullWidth` swaps `inline-flex` for block-level `flex`. + * + * The chip carries NO outer margin — spacing between chips belongs to the parent, as a `gap`. It used to ship a + * default `mx-0.5` "cluster margin" with a `flush` prop to switch it off, which meant a chip's visual box was not + * its layout box: changing the space between two chips took an edit in two places, and a collapsing container + * could never close past the margins. Do not reintroduce it. * * The default/filled hover lives in `active`-keyed compound variants (not the base variant string) so the * rest/hover classes are mutually exclusive — a chip renders exactly ONE `hover-hover:bg-*`. This keeps raw @@ -62,7 +66,6 @@ const chipVariants = cva( }, active: { true: '', false: '' }, fullWidth: { true: 'flex', false: 'inline-flex' }, - flush: { true: 'mx-0', false: 'mx-0.5' }, }, compoundVariants: [ { @@ -86,7 +89,7 @@ const chipVariants = cva( className: 'bg-[var(--surface-active)] hover-hover:bg-[var(--surface-6)]', }, ], - defaultVariants: { variant: 'default', active: false, fullWidth: false, flush: false }, + defaultVariants: { variant: 'default', active: false, fullWidth: false }, } ) @@ -142,14 +145,14 @@ interface ChipProps * @example {balance} */ const Chip = forwardRef(function Chip( - { className, variant, active, fullWidth, flush, leftIcon, rightIcon, children, type, ...props }, + { className, variant, active, fullWidth, leftIcon, rightIcon, children, type, ...props }, ref ) { return (
diff --git a/packages/emcn/src/components/tag-input/tag-input.tsx b/packages/emcn/src/components/tag-input/tag-input.tsx index 36f6de7f1d6..6cee52f3be3 100644 --- a/packages/emcn/src/components/tag-input/tag-input.tsx +++ b/packages/emcn/src/components/tag-input/tag-input.tsx @@ -39,7 +39,7 @@ import * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority' -import { Paperclip, Plus, X } from 'lucide-react' +import { Paperclip, Plus, X } from '../../icons' import { cn } from '../../lib/cn' import { handleKeyboardActivation } from '../../lib/keyboard' import { ChipTag, chipTagVariants } from '../chip-tag/chip-tag' @@ -99,7 +99,7 @@ export interface FileInputOptions { /** Accepted file types (default: '.csv,.txt,text/csv,text/plain') */ accept?: string /** Icon component to render (default: Paperclip) */ - icon?: React.ComponentType<{ className?: string; strokeWidth?: number }> + icon?: React.ComponentType<{ className?: string }> /** Extract values from file content. Each extracted value will be passed to onAdd. */ extractValues?: (text: string) => string[] /** Tooltip text for the file input button */ @@ -492,7 +492,7 @@ const TagInput = React.forwardRef( className='-m-1.5 absolute right-2 bottom-[9px] p-1.5 text-[var(--text-tertiary)] transition-colors hover-hover:text-[var(--text-secondary)]' aria-label={fileInputOptions?.tooltip ?? 'Upload file'} > - + diff --git a/packages/emcn/src/components/time-picker/time-picker.tsx b/packages/emcn/src/components/time-picker/time-picker.tsx index f12462ba9ce..8b6ccb0bfaa 100644 --- a/packages/emcn/src/components/time-picker/time-picker.tsx +++ b/packages/emcn/src/components/time-picker/time-picker.tsx @@ -27,7 +27,7 @@ import * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority' -import { ChevronDown } from 'lucide-react' +import { ChevronDown } from '../../icons' import { cn } from '../../lib/cn' import { Popover, PopoverAnchor, PopoverContent } from '../popover/popover' diff --git a/packages/emcn/src/components/toast/toast.tsx b/packages/emcn/src/components/toast/toast.tsx index 5f8cb5a8a86..4798688cfc0 100644 --- a/packages/emcn/src/components/toast/toast.tsx +++ b/packages/emcn/src/components/toast/toast.tsx @@ -339,7 +339,7 @@ function ToastItem({ toast: t, geometry, reduceMotion, onDismiss, onMeasure }: T onClick={dismiss} aria-label='Dismiss notification' title='Dismiss' - className='size-[18px] rounded-sm p-0' + size='icon' > @@ -358,7 +358,6 @@ function ToastItem({ toast: t, geometry, reduceMotion, onDismiss, onMeasure }: T {t.action ? ( { t.action!.onClick() dismiss() diff --git a/packages/emcn/src/icons/user-plus.tsx b/packages/emcn/src/icons/arrow-left-right.tsx similarity index 56% rename from packages/emcn/src/icons/user-plus.tsx rename to packages/emcn/src/icons/arrow-left-right.tsx index d2c6a30ea71..b29836ab80b 100644 --- a/packages/emcn/src/icons/user-plus.tsx +++ b/packages/emcn/src/icons/arrow-left-right.tsx @@ -1,10 +1,10 @@ import type { SVGProps } from 'react' /** - * UserPlus icon component — person silhouette with a plus sign in the bottom-right + * ArrowLeftRight icon component - stacked left and right arrows for swap toggles * @param props - SVG properties including className, fill, etc. */ -export function UserPlus(props: SVGProps) { +export function ArrowLeftRight(props: SVGProps) { return ( ) { aria-hidden='true' {...props} > - - - - + + + + ) } diff --git a/packages/emcn/src/icons/arrow-up-left.tsx b/packages/emcn/src/icons/arrow-up-left.tsx new file mode 100644 index 00000000000..3410ab3fb89 --- /dev/null +++ b/packages/emcn/src/icons/arrow-up-left.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * ArrowUpLeft icon component - diagonal arrow pointing to the upper-left corner + * @param props - SVG properties including className, fill, etc. + */ +export function ArrowUpLeft(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/ban.tsx b/packages/emcn/src/icons/ban.tsx new file mode 100644 index 00000000000..42886de1c0d --- /dev/null +++ b/packages/emcn/src/icons/ban.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * Ban icon component - prohibition sign, a circle crossed by one diagonal bar + * @param props - SVG properties including className, fill, etc. + */ +export function Ban(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/bold.tsx b/packages/emcn/src/icons/bold.tsx new file mode 100644 index 00000000000..bbc6754a3d8 --- /dev/null +++ b/packages/emcn/src/icons/bold.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * Bold icon component - capital B with a vertical stem and two stacked bowls + * @param props - SVG properties including className, fill, etc. + */ +export function Bold(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/box.tsx b/packages/emcn/src/icons/box.tsx new file mode 100644 index 00000000000..32cab06e83c --- /dev/null +++ b/packages/emcn/src/icons/box.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Box icon component - isometric 3D cube + * @param props - SVG properties including className, fill, etc. + */ +export function Box(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/brain-circuit.tsx b/packages/emcn/src/icons/brain-circuit.tsx new file mode 100644 index 00000000000..aa0430d194e --- /dev/null +++ b/packages/emcn/src/icons/brain-circuit.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * BrainCircuit icon component - half brain with circuit traces branching to node dots + * @param props - SVG properties including className, fill, etc. + */ +export function BrainCircuit(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/brain.tsx b/packages/emcn/src/icons/brain.tsx new file mode 100644 index 00000000000..98bbcee58ad --- /dev/null +++ b/packages/emcn/src/icons/brain.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Brain icon component - two mirrored lobed halves joined by a central seam + * @param props - SVG properties including className, fill, etc. + */ +export function Brain(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/bubble-chat-delay.tsx b/packages/emcn/src/icons/bubble-chat-delay.tsx index 490cf2b7f83..02cfa105458 100644 --- a/packages/emcn/src/icons/bubble-chat-delay.tsx +++ b/packages/emcn/src/icons/bubble-chat-delay.tsx @@ -25,7 +25,7 @@ export function BubbleChatDelay({ size = 24, width, height, ...props }: IconProp stroke='currentColor' strokeLinecap='round' strokeLinejoin='round' - strokeWidth='1.5' + strokeWidth='1.55' /> ) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/camera.tsx b/packages/emcn/src/icons/camera.tsx new file mode 100644 index 00000000000..461520ce560 --- /dev/null +++ b/packages/emcn/src/icons/camera.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Camera icon component - camera body with a raised viewfinder bump and a centered lens + * @param props - SVG properties including className, fill, etc. + */ +export function Camera(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/chevron-up.tsx b/packages/emcn/src/icons/chevron-up.tsx new file mode 100644 index 00000000000..ae77e46b4ba --- /dev/null +++ b/packages/emcn/src/icons/chevron-up.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * ChevronUp icon component - single chevron pointing up + * @param props - SVG properties including className, fill, etc. + */ +export function ChevronUp(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/chevrons-down-up.tsx b/packages/emcn/src/icons/chevrons-down-up.tsx new file mode 100644 index 00000000000..e6817a32ebb --- /dev/null +++ b/packages/emcn/src/icons/chevrons-down-up.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * ChevronsDownUp icon component - stacked chevrons pointing toward each other + * @param props - SVG properties including className, fill, etc. + */ +export function ChevronsDownUp(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/chevrons-up-down.tsx b/packages/emcn/src/icons/chevrons-up-down.tsx new file mode 100644 index 00000000000..62d52512cc5 --- /dev/null +++ b/packages/emcn/src/icons/chevrons-up-down.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * ChevronsUpDown icon component - stacked chevrons pointing away from each other + * @param props - SVG properties including className, fill, etc. + */ +export function ChevronsUpDown(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/circle-pause.tsx b/packages/emcn/src/icons/circle-pause.tsx new file mode 100644 index 00000000000..0d5915446ce --- /dev/null +++ b/packages/emcn/src/icons/circle-pause.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * CirclePause icon component - circle enclosing two vertical bars, used for waiting states + * @param props - SVG properties including className, fill, etc. + */ +export function CirclePause(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/circle-x.tsx b/packages/emcn/src/icons/circle-x.tsx new file mode 100644 index 00000000000..73a83c8f307 --- /dev/null +++ b/packages/emcn/src/icons/circle-x.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * CircleX icon component - circle enclosing an X, used for error and failed states + * @param props - SVG properties including className, fill, etc. + */ +export function CircleX(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/circle.tsx b/packages/emcn/src/icons/circle.tsx new file mode 100644 index 00000000000..910c0803309 --- /dev/null +++ b/packages/emcn/src/icons/circle.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * Circle icon component - plain unfilled ring, fills when given `fill-current` + * @param props - SVG properties including className, fill, etc. + */ +export function Circle(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/clipboard.tsx b/packages/emcn/src/icons/clipboard.tsx index 1bf26d7af58..9683e9a445a 100644 --- a/packages/emcn/src/icons/clipboard.tsx +++ b/packages/emcn/src/icons/clipboard.tsx @@ -12,7 +12,7 @@ export function Clipboard(props: SVGProps) { viewBox='-1 -2 24 24' fill='none' stroke='currentColor' - strokeWidth='1.75' + strokeWidth='1.55' strokeLinecap='round' strokeLinejoin='round' xmlns='http://www.w3.org/2000/svg' diff --git a/packages/emcn/src/icons/code.tsx b/packages/emcn/src/icons/code.tsx new file mode 100644 index 00000000000..23ca3105d31 --- /dev/null +++ b/packages/emcn/src/icons/code.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * Code icon component - two angle brackets pointing away from a shared centre + * @param props - SVG properties including className, fill, etc. + */ +export function Code(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/compass.tsx b/packages/emcn/src/icons/compass.tsx new file mode 100644 index 00000000000..81ddda7ae3a --- /dev/null +++ b/packages/emcn/src/icons/compass.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Compass icon component - dial with a two-tone needle pointing north-east + * @param props - SVG properties including className, fill, etc. + */ +export function Compass(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/credit.tsx b/packages/emcn/src/icons/credit.tsx index 5e5a4e0c43b..98b0c3ee25c 100644 --- a/packages/emcn/src/icons/credit.tsx +++ b/packages/emcn/src/icons/credit.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Credit icon component - circular token with inner ring + * Credit icon component - coin with an inner ring * @param props - SVG properties including className, fill, etc. */ export function Credit(props: SVGProps) { @@ -19,8 +19,8 @@ export function Credit(props: SVGProps) { aria-hidden='true' {...props} > - - + + ) } diff --git a/packages/emcn/src/icons/database.tsx b/packages/emcn/src/icons/database.tsx index d0a221cdeb2..415e8ada1a5 100644 --- a/packages/emcn/src/icons/database.tsx +++ b/packages/emcn/src/icons/database.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Database icon component - cylinder with horizontal dividers + * Database icon component - cylinder with a single tier divider * @param props - SVG properties including className, fill, etc. */ export function Database(props: SVGProps) { @@ -19,9 +19,9 @@ export function Database(props: SVGProps) { aria-hidden='true' {...props} > - - - + + + ) } diff --git a/packages/emcn/src/icons/eye-off.tsx b/packages/emcn/src/icons/eye-off.tsx index 62cc6922ae8..1196edad71c 100644 --- a/packages/emcn/src/icons/eye-off.tsx +++ b/packages/emcn/src/icons/eye-off.tsx @@ -17,7 +17,7 @@ export function EyeOff(props: SVGProps) { viewBox='0 0 24 24' fill='none' stroke='currentColor' - strokeWidth='2' + strokeWidth='1.55' strokeLinecap='round' strokeLinejoin='round' xmlns='http://www.w3.org/2000/svg' diff --git a/packages/emcn/src/icons/file-text.tsx b/packages/emcn/src/icons/file-text.tsx new file mode 100644 index 00000000000..c47d022e608 --- /dev/null +++ b/packages/emcn/src/icons/file-text.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * FileText icon component - document with folded corner and text lines + * @param props - SVG properties including className, fill, etc. + */ +export function FileText(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/files.tsx b/packages/emcn/src/icons/files.tsx index c7ed29bfdbb..ac2226b6f3a 100644 --- a/packages/emcn/src/icons/files.tsx +++ b/packages/emcn/src/icons/files.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Files icon component - stacked documents with folded corner on the front doc + * Files icon component - a document with a folded corner over a second sheet * @param props - SVG properties including className, fill, etc. */ export function Files(props: SVGProps) { @@ -19,9 +19,9 @@ export function Files(props: SVGProps) { aria-hidden='true' {...props} > - - - + + + ) } diff --git a/packages/emcn/src/icons/folder-input.tsx b/packages/emcn/src/icons/folder-input.tsx index 3a47e4e9b1c..022eecc6cf7 100644 --- a/packages/emcn/src/icons/folder-input.tsx +++ b/packages/emcn/src/icons/folder-input.tsx @@ -14,7 +14,7 @@ export function FolderInput(props: SVGProps) { diff --git a/packages/emcn/src/icons/folder-open.tsx b/packages/emcn/src/icons/folder-open.tsx new file mode 100644 index 00000000000..66ad48cb6b5 --- /dev/null +++ b/packages/emcn/src/icons/folder-open.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * FolderOpen icon component - folder with its front panel tilted open + * @param props - SVG properties including className, fill, etc. + */ +export function FolderOpen(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/form-input.tsx b/packages/emcn/src/icons/form-input.tsx new file mode 100644 index 00000000000..fd3ada471ad --- /dev/null +++ b/packages/emcn/src/icons/form-input.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * FormInput icon component - input field with a text caret and placeholder dashes + * @param props - SVG properties including className, fill, etc. + */ +export function FormInput(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/globe.tsx b/packages/emcn/src/icons/globe.tsx new file mode 100644 index 00000000000..0e6a5fc99c2 --- /dev/null +++ b/packages/emcn/src/icons/globe.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Globe icon component - circle with a meridian ellipse and two latitude lines + * @param props - SVG properties including className, fill, etc. + */ +export function Globe(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/heading1.tsx b/packages/emcn/src/icons/heading1.tsx new file mode 100644 index 00000000000..f5542145d86 --- /dev/null +++ b/packages/emcn/src/icons/heading1.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Heading1 icon component - capital H with a numeral 1 set to its right + * @param props - SVG properties including className, fill, etc. + */ +export function Heading1(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/heading2.tsx b/packages/emcn/src/icons/heading2.tsx new file mode 100644 index 00000000000..d3766c61b83 --- /dev/null +++ b/packages/emcn/src/icons/heading2.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Heading2 icon component - capital H with a numeral 2 set to its right + * @param props - SVG properties including className, fill, etc. + */ +export function Heading2(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/heading3.tsx b/packages/emcn/src/icons/heading3.tsx new file mode 100644 index 00000000000..bc1e7aec949 --- /dev/null +++ b/packages/emcn/src/icons/heading3.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Heading3 icon component - capital H with a numeral 3 set to its right + * @param props - SVG properties including className, fill, etc. + */ +export function Heading3(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/help-circle.tsx b/packages/emcn/src/icons/help-circle.tsx index 8ea21b8f8cd..4ed81d38d0c 100644 --- a/packages/emcn/src/icons/help-circle.tsx +++ b/packages/emcn/src/icons/help-circle.tsx @@ -9,7 +9,7 @@ export function HelpCircle(props: SVGProps) { ) { aria-hidden='true' {...props} > - - - + + + ) } diff --git a/packages/emcn/src/icons/highlighter.tsx b/packages/emcn/src/icons/highlighter.tsx new file mode 100644 index 00000000000..3b14d9e5776 --- /dev/null +++ b/packages/emcn/src/icons/highlighter.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Highlighter icon component - chisel-tip marker angled up-right above a highlighted bar + * @param props - SVG properties including className, fill, etc. + */ +export function Highlighter(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/index.ts b/packages/emcn/src/icons/index.ts index 1f871b6f680..5967c67ef30 100644 --- a/packages/emcn/src/icons/index.ts +++ b/packages/emcn/src/icons/index.ts @@ -1,29 +1,46 @@ export { ArrowDown } from './arrow-down' export { ArrowLeft } from './arrow-left' +export { ArrowLeftRight } from './arrow-left-right' export { ArrowRight } from './arrow-right' export { ArrowUp } from './arrow-up' export { ArrowUpDown } from './arrow-up-down' +export { ArrowUpLeft } from './arrow-up-left' export { Asterisk } from './asterisk' +export { Ban } from './ban' export { Bell } from './bell' export { Blimp } from './blimp' +export { Bold } from './bold' export { BookOpen } from './book-open' +export { Box } from './box' +export { Brain } from './brain' +export { BrainCircuit } from './brain-circuit' export { BubbleChatClose } from './bubble-chat-close' export { BubbleChatDelay } from './bubble-chat-delay' export { BubbleChatPreview } from './bubble-chat-preview' export { Bug } from './bug' +export { Building } from './building' export { Calendar } from './calendar' +export { Camera } from './camera' export { Check } from './check' export { ChevronDown } from './chevron-down' export { ChevronLeft } from './chevron-left' export { ChevronRight } from './chevron-right' +export { ChevronUp } from './chevron-up' +export { ChevronsDownUp } from './chevrons-down-up' +export { ChevronsUpDown } from './chevrons-up-down' +export { Circle } from './circle' export { CircleAlert } from './circle-alert' export { CircleCheck } from './circle-check' export { CircleInfo } from './circle-info' +export { CirclePause } from './circle-pause' +export { CircleX } from './circle-x' export { Clipboard } from './clipboard' export { ClipboardList } from './clipboard-list' export { Clock } from './clock' +export { Code } from './code' export { Columns2 } from './columns2' export { Columns3 } from './columns3' +export { Compass } from './compass' export { Connections } from './connections' export { Credit } from './credit' export { Cursor } from './cursor' @@ -36,40 +53,62 @@ export { Expand } from './expand' export { Eye } from './eye' export { EyeOff } from './eye-off' export { File } from './file' +export { FileText } from './file-text' export { FileX } from './file-x' export { Files } from './files' export { Fingerprint } from './fingerprint' export { Folder } from './folder' export { FolderCode } from './folder-code' export { FolderInput } from './folder-input' +export { FolderOpen } from './folder-open' export { FolderPlus } from './folder-plus' +export { FormInput } from './form-input' +export { Globe } from './globe' export { Hammer } from './hammer' export { Hand } from './hand' +export { Heading1 } from './heading1' +export { Heading2 } from './heading2' +export { Heading3 } from './heading3' export { HelpCircle } from './help-circle' export { HexSimple } from './hex-simple' +export { Highlighter } from './highlighter' export { Home } from './home' export { ImageUp } from './image-up' +export { InfinityIcon, InfinityIcon as Infinity } from './infinity' export { Integration } from './integration' +export { Italic } from './italic' export { Key } from './key' export { KeySquare } from './key-square' export { Layout } from './layout' export { Library } from './library' export { Link } from './link' +export { List } from './list' +export { ListChecks } from './list-checks' export { ListFilter } from './list-filter' +export { ListOrdered } from './list-ordered' export { Loader } from './loader' export { Lock } from './lock' export { LogIn } from './log-in' export { LogOut } from './log-out' export { Mail } from './mail' export { ManageWorkspace } from './manage-workspace' +export { Menu } from './menu' export { Mic } from './mic' +export { MicOff } from './mic-off' +export { Minus } from './minus' +export { Moon } from './moon' export { MoreHorizontal } from './more-horizontal' +export { MoreVertical } from './more-vertical' +export { Music } from './music' export { NoWrap } from './no-wrap' export { Palette } from './palette' export { PanelLeft } from './panel-left' +export { PanelRight } from './panel-right' export { Paperclip } from './paperclip' export { Pause } from './pause' export { Pencil } from './pencil' +export { Phone } from './phone' +export { Pilcrow } from './pilcrow' export { PillsRing } from './pills-ring' export { Pin } from './pin' export { PinOff } from './pin-off' @@ -77,30 +116,37 @@ export { Play, PlayOutline } from './play' export { Plus } from './plus' export { Redo } from './redo' export { RefreshCw } from './refresh-cw' +export { Repeat } from './repeat' export { Rocket } from './rocket' export { Rows3 } from './rows3' +export { Rss } from './rss' +export { Scissors } from './scissors' export { Search } from './search' export { SelectAll } from './select-all' export { Send } from './send' +export { SendToBack } from './send-to-back' export { Server } from './server' export { Settings } from './settings' +export { Share } from './share' export { ShieldCheck } from './shield-check' export { Shuffle } from './shuffle' export { Sim } from './sim' export { Slash } from './slash' +export { Sparkles } from './sparkles' export { Split } from './split' export { Square } from './square' export { SquareArrowUpRight } from './square-arrow-up-right' +export { Strikethrough } from './strikethrough' +export { Sun } from './sun' export { Table } from './table' export { TableX } from './table-x' export { TagIcon } from './tag' export { Task } from './task' export { TerminalWindow } from './terminal-window' +export { TextQuote } from './text-quote' export { ThumbsDown } from './thumbs-down' export { ThumbsUp } from './thumbs-up' export { Trash } from './trash' -export { TrashOutline } from './trash-outline' -export { Trash2 } from './trash2' export { TriangleAlert } from './triangle-alert' export { TypeBoolean } from './type-boolean' export { TypeCurrency } from './type-currency' @@ -108,16 +154,19 @@ export { TypeJson } from './type-json' export { TypeNumber } from './type-number' export { TypeText } from './type-text' export { Undo } from './undo' +export { Unlink } from './unlink' export { Unlock } from './unlock' export { Upload } from './upload' export { User } from './user' -export { UserPlus } from './user-plus' export { Users } from './users' +export { Wand } from './wand' +export { Webhook } from './webhook' export { Wordmark } from './wordmark' export { Workflow } from './workflow' export { WorkflowX } from './workflow-x' export { Wrap } from './wrap' export { Wrench } from './wrench' export { X } from './x' +export { Zap } from './zap' export { ZoomIn } from './zoom-in' export { ZoomOut } from './zoom-out' diff --git a/packages/emcn/src/icons/infinity.tsx b/packages/emcn/src/icons/infinity.tsx new file mode 100644 index 00000000000..8a0bd923eff --- /dev/null +++ b/packages/emcn/src/icons/infinity.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * Infinity icon component - lemniscate drawn as one continuous stroke + * @param props - SVG properties including className, fill, etc. + */ +export function InfinityIcon(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/italic.tsx b/packages/emcn/src/icons/italic.tsx new file mode 100644 index 00000000000..abc0855a821 --- /dev/null +++ b/packages/emcn/src/icons/italic.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Italic icon component - slanted stroke with short serifs at its top and bottom + * @param props - SVG properties including className, fill, etc. + */ +export function Italic(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/library.tsx b/packages/emcn/src/icons/library.tsx index 1bd3d0b8428..13eab8d6d84 100644 --- a/packages/emcn/src/icons/library.tsx +++ b/packages/emcn/src/icons/library.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Library icon component - displays stacked books/documents + * Library icon component - stacked log entries, each a short marker beside its line * @param props - SVG properties including className, fill, etc. */ export function Library(props: SVGProps) { @@ -19,10 +19,12 @@ export function Library(props: SVGProps) { aria-hidden='true' {...props} > - - - - + + + + + + ) } diff --git a/packages/emcn/src/icons/list-checks.tsx b/packages/emcn/src/icons/list-checks.tsx new file mode 100644 index 00000000000..264a2602d19 --- /dev/null +++ b/packages/emcn/src/icons/list-checks.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * ListChecks icon component - three lines, each preceded by a checkmark + * @param props - SVG properties including className, fill, etc. + */ +export function ListChecks(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/list-ordered.tsx b/packages/emcn/src/icons/list-ordered.tsx new file mode 100644 index 00000000000..32e8728efee --- /dev/null +++ b/packages/emcn/src/icons/list-ordered.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * ListOrdered icon component - three lines, each preceded by the numerals 1, 2 and 3 + * @param props - SVG properties including className, fill, etc. + */ +export function ListOrdered(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/list.tsx b/packages/emcn/src/icons/list.tsx new file mode 100644 index 00000000000..77bf53df303 --- /dev/null +++ b/packages/emcn/src/icons/list.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * List icon component - three lines, each preceded by a round bullet dot + * @param props - SVG properties including className, fill, etc. + */ +export function List(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/menu.tsx b/packages/emcn/src/icons/menu.tsx new file mode 100644 index 00000000000..fc2c5cdf47e --- /dev/null +++ b/packages/emcn/src/icons/menu.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Menu icon component - three equal-length horizontal bars (hamburger) + * @param props - SVG properties including className, fill, etc. + */ +export function Menu(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/mic-off.tsx b/packages/emcn/src/icons/mic-off.tsx new file mode 100644 index 00000000000..a434155ad7d --- /dev/null +++ b/packages/emcn/src/icons/mic-off.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * MicOff icon component - microphone with diagonal strike-through for the muted state + * @param props - SVG properties including className, fill, etc. + */ +export function MicOff(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/minus.tsx b/packages/emcn/src/icons/minus.tsx new file mode 100644 index 00000000000..ddab0bbaf84 --- /dev/null +++ b/packages/emcn/src/icons/minus.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * Minus icon component - single horizontal bar matching the horizontal stroke of Plus + * @param props - SVG properties including className, fill, etc. + */ +export function Minus(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/moon.tsx b/packages/emcn/src/icons/moon.tsx new file mode 100644 index 00000000000..aa36f144530 --- /dev/null +++ b/packages/emcn/src/icons/moon.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * Moon icon component - crescent moon for dark mode + * @param props - SVG properties including className, fill, etc. + */ +export function Moon(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/more-vertical.tsx b/packages/emcn/src/icons/more-vertical.tsx new file mode 100644 index 00000000000..1d8335716ff --- /dev/null +++ b/packages/emcn/src/icons/more-vertical.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * MoreVertical icon component - three evenly spaced vertical dots + * @param props - SVG properties including className, fill, etc. + */ +export function MoreVertical(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/music.tsx b/packages/emcn/src/icons/music.tsx new file mode 100644 index 00000000000..b746acc7407 --- /dev/null +++ b/packages/emcn/src/icons/music.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Music icon component - two eighth notes joined by a single beam + * @param props - SVG properties including className, fill, etc. + */ +export function Music(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/panel-right.tsx b/packages/emcn/src/icons/panel-right.tsx new file mode 100644 index 00000000000..b862215be85 --- /dev/null +++ b/packages/emcn/src/icons/panel-right.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * PanelRight icon component - sidebar panel docked to the right edge + * @param props - SVG properties including className, fill, etc. + */ +export function PanelRight(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/phone.tsx b/packages/emcn/src/icons/phone.tsx new file mode 100644 index 00000000000..f190ecf2ca6 --- /dev/null +++ b/packages/emcn/src/icons/phone.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * Phone icon component - angled handset outline, centred so a 135 degree rotation reads as hang up + * @param props - SVG properties including className, fill, etc. + */ +export function Phone(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/pilcrow.tsx b/packages/emcn/src/icons/pilcrow.tsx new file mode 100644 index 00000000000..44bd955bb76 --- /dev/null +++ b/packages/emcn/src/icons/pilcrow.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Pilcrow icon component - paragraph mark with a closed bowl and two descending stems + * @param props - SVG properties including className, fill, etc. + */ +export function Pilcrow(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/pin-off.tsx b/packages/emcn/src/icons/pin-off.tsx index 0f1bf606275..3f824d8f97a 100644 --- a/packages/emcn/src/icons/pin-off.tsx +++ b/packages/emcn/src/icons/pin-off.tsx @@ -13,7 +13,7 @@ export function PinOff(props: SVGProps) { viewBox='0 0 24 24' fill='none' stroke='currentColor' - strokeWidth='1.75' + strokeWidth='1.55' strokeLinecap='round' strokeLinejoin='round' aria-hidden='true' diff --git a/packages/emcn/src/icons/pin.tsx b/packages/emcn/src/icons/pin.tsx index 0e9fbfec2a0..12168b5675f 100644 --- a/packages/emcn/src/icons/pin.tsx +++ b/packages/emcn/src/icons/pin.tsx @@ -13,7 +13,7 @@ export function Pin(props: SVGProps) { viewBox='0 0 24 24' fill='none' stroke='currentColor' - strokeWidth='1.75' + strokeWidth='1.55' strokeLinecap='round' strokeLinejoin='round' aria-hidden='true' diff --git a/packages/emcn/src/icons/repeat.tsx b/packages/emcn/src/icons/repeat.tsx new file mode 100644 index 00000000000..31d1f51ec12 --- /dev/null +++ b/packages/emcn/src/icons/repeat.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Repeat icon component - two arrows joined into a rectangular loop + * @param props - SVG properties including className, fill, etc. + */ +export function Repeat(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/rss.tsx b/packages/emcn/src/icons/rss.tsx new file mode 100644 index 00000000000..4336e0dc59c --- /dev/null +++ b/packages/emcn/src/icons/rss.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Rss icon component - two concentric quarter arcs radiating from a corner dot + * @param props - SVG properties including className, fill, etc. + */ +export function Rss(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/scissors.tsx b/packages/emcn/src/icons/scissors.tsx new file mode 100644 index 00000000000..f68fc1a8f9d --- /dev/null +++ b/packages/emcn/src/icons/scissors.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Scissors icon component - crossed blades with ring handles + * @param props - SVG properties including className, fill, etc. + */ +export function Scissors(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/select-all.tsx b/packages/emcn/src/icons/select-all.tsx index a57f4a87c8f..752177bf8d6 100644 --- a/packages/emcn/src/icons/select-all.tsx +++ b/packages/emcn/src/icons/select-all.tsx @@ -12,7 +12,7 @@ export function SelectAll(props: SVGProps) { viewBox='0 0 24 24' fill='none' stroke='currentColor' - strokeWidth='2' + strokeWidth='1.55' strokeLinecap='round' strokeLinejoin='round' xmlns='http://www.w3.org/2000/svg' diff --git a/packages/emcn/src/icons/send-to-back.tsx b/packages/emcn/src/icons/send-to-back.tsx new file mode 100644 index 00000000000..4d9515107a9 --- /dev/null +++ b/packages/emcn/src/icons/send-to-back.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * SendToBack icon component - a solid rounded square in front of a clipped one behind it + * @param props - SVG properties including className, fill, etc. + */ +export function SendToBack(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/send.tsx b/packages/emcn/src/icons/send.tsx index 3ec578aafd6..e29808cce5b 100644 --- a/packages/emcn/src/icons/send.tsx +++ b/packages/emcn/src/icons/send.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Send icon component - paper plane / arrow + * Send icon component - paper plane * @param props - SVG properties including className, fill, etc. */ export function Send(props: SVGProps) { @@ -19,8 +19,8 @@ export function Send(props: SVGProps) { aria-hidden='true' {...props} > - - + + ) } diff --git a/packages/emcn/src/icons/settings.tsx b/packages/emcn/src/icons/settings.tsx index 8064409535b..42925fdd7f9 100644 --- a/packages/emcn/src/icons/settings.tsx +++ b/packages/emcn/src/icons/settings.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Settings icon component - gear/cog + * Settings icon component - six-tooth gear with a hub * @param props - SVG properties including className, fill, etc. */ export function Settings(props: SVGProps) { @@ -9,7 +9,7 @@ export function Settings(props: SVGProps) { ) { aria-hidden='true' {...props} > - - + + ) } diff --git a/packages/emcn/src/icons/share.tsx b/packages/emcn/src/icons/share.tsx new file mode 100644 index 00000000000..2324fb9a1e6 --- /dev/null +++ b/packages/emcn/src/icons/share.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Share icon component - three nodes joined by two connecting lines + * @param props - SVG properties including className, fill, etc. + */ +export function Share(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/sparkles.tsx b/packages/emcn/src/icons/sparkles.tsx new file mode 100644 index 00000000000..d5dde83c6bd --- /dev/null +++ b/packages/emcn/src/icons/sparkles.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Sparkles icon component - one large four-pointed star with two smaller ones + * @param props - SVG properties including className, fill, etc. + */ +export function Sparkles(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/square.tsx b/packages/emcn/src/icons/square.tsx index 66bea3a4a09..2e3a45d5177 100644 --- a/packages/emcn/src/icons/square.tsx +++ b/packages/emcn/src/icons/square.tsx @@ -3,8 +3,8 @@ import type { SVGProps } from 'react' /** * Square icon (stroke/outline version) — used as the "stop" glyph in * media-control-style buttons (per-row run/stop, table action bar, context - * menus). Same visual as `lucide-react`'s `Square` so existing call sites - * migrate without any visual change. + * menus). Drawn as a plain rounded square so it reads as a neutral stop mark + * next to `Play` in the same control cluster. */ export function Square(props: SVGProps) { return ( @@ -14,7 +14,7 @@ export function Square(props: SVGProps) { viewBox='0 0 24 24' fill='none' stroke='currentColor' - strokeWidth='1.75' + strokeWidth='1.55' strokeLinecap='round' strokeLinejoin='round' xmlns='http://www.w3.org/2000/svg' diff --git a/packages/emcn/src/icons/strikethrough.tsx b/packages/emcn/src/icons/strikethrough.tsx new file mode 100644 index 00000000000..f4b1f093678 --- /dev/null +++ b/packages/emcn/src/icons/strikethrough.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * Strikethrough icon component - capital S cut by a full-width horizontal rule + * @param props - SVG properties including className, fill, etc. + */ +export function Strikethrough(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/sun.tsx b/packages/emcn/src/icons/sun.tsx new file mode 100644 index 00000000000..8539a5a5183 --- /dev/null +++ b/packages/emcn/src/icons/sun.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Sun icon component - centered disc with eight radiating rays for light mode + * @param props - SVG properties including className, fill, etc. + */ +export function Sun(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/table.tsx b/packages/emcn/src/icons/table.tsx index 57be040a2c2..f9a585ba0a5 100644 --- a/packages/emcn/src/icons/table.tsx +++ b/packages/emcn/src/icons/table.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Table icon component - displays a grid table + * Table icon component - rounded frame with a header row and a column divider * @param props - SVG properties including className, fill, etc. */ export function Table(props: SVGProps) { @@ -22,7 +22,7 @@ export function Table(props: SVGProps) { - + ) } diff --git a/packages/emcn/src/icons/task.tsx b/packages/emcn/src/icons/task.tsx index 85b39b243b8..8df5039a0aa 100644 --- a/packages/emcn/src/icons/task.tsx +++ b/packages/emcn/src/icons/task.tsx @@ -1,26 +1,25 @@ import type { SVGProps } from 'react' /** - * Task icon component - inbox tray representing incoming tasks + * Task icon component - rounded speech bubble with a tail * @param props - SVG properties including className, fill, etc. */ export function Task(props: SVGProps) { return ( ) } diff --git a/packages/emcn/src/icons/text-quote.tsx b/packages/emcn/src/icons/text-quote.tsx new file mode 100644 index 00000000000..56e9fa0305b --- /dev/null +++ b/packages/emcn/src/icons/text-quote.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from 'react' + +/** + * TextQuote icon component - two full lines above two indented lines marked by a quote bar + * @param props - SVG properties including className, fill, etc. + */ +export function TextQuote(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/trash-outline.tsx b/packages/emcn/src/icons/trash-outline.tsx deleted file mode 100644 index 7b112541b96..00000000000 --- a/packages/emcn/src/icons/trash-outline.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { SVGProps } from 'react' - -/** - * Outline-style trash icon matching the stroke-based nav icon convention - * @param props - SVG properties including className, fill, etc. - */ -export function TrashOutline(props: SVGProps) { - return ( - - ) -} diff --git a/packages/emcn/src/icons/trash.tsx b/packages/emcn/src/icons/trash.tsx index 6c9a9fc078b..015efa97a19 100644 --- a/packages/emcn/src/icons/trash.tsx +++ b/packages/emcn/src/icons/trash.tsx @@ -1,24 +1,27 @@ import type { SVGProps } from 'react' /** - * Trash icon component + * Trash icon component - lidded bin * @param props - SVG properties including className, fill, etc. */ export function Trash(props: SVGProps) { return ( ) } diff --git a/packages/emcn/src/icons/trash2.tsx b/packages/emcn/src/icons/trash2.tsx deleted file mode 100644 index 9148d809864..00000000000 --- a/packages/emcn/src/icons/trash2.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { SVGProps } from 'react' - -/** - * Trash2 icon component - * @param props - SVG properties including className, fill, etc. - */ -export function Trash2(props: SVGProps) { - return ( - - ) -} diff --git a/packages/emcn/src/icons/unlink.tsx b/packages/emcn/src/icons/unlink.tsx new file mode 100644 index 00000000000..884d9044acb --- /dev/null +++ b/packages/emcn/src/icons/unlink.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Unlink icon component - a broken chain, two half links pulled apart with break marks + * @param props - SVG properties including className, fill, etc. + */ +export function Unlink(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/users.tsx b/packages/emcn/src/icons/users.tsx index 2169b250b1e..3605b1d51b6 100644 --- a/packages/emcn/src/icons/users.tsx +++ b/packages/emcn/src/icons/users.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Users icon component - two person silhouettes + * Users icon component - two person silhouettes side by side * @param props - SVG properties including className, fill, etc. */ export function Users(props: SVGProps) { @@ -19,10 +19,10 @@ export function Users(props: SVGProps) { aria-hidden='true' {...props} > - - - - + + + + ) } diff --git a/packages/emcn/src/icons/wand.tsx b/packages/emcn/src/icons/wand.tsx new file mode 100644 index 00000000000..bcf9bdddd85 --- /dev/null +++ b/packages/emcn/src/icons/wand.tsx @@ -0,0 +1,27 @@ +import type { SVGProps } from 'react' + +/** + * Wand icon component - angled wand with sparkles at its tip + * @param props - SVG properties including className, fill, etc. + */ +export function Wand(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/webhook.tsx b/packages/emcn/src/icons/webhook.tsx new file mode 100644 index 00000000000..457e178251c --- /dev/null +++ b/packages/emcn/src/icons/webhook.tsx @@ -0,0 +1,28 @@ +import type { SVGProps } from 'react' + +/** + * Webhook icon component - three arcs radiating from a shared centre into node dots + * @param props - SVG properties including className, fill, etc. + */ +export function Webhook(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/icons/workflow.tsx b/packages/emcn/src/icons/workflow.tsx index 6ee0d55c27c..b31622ae9c6 100644 --- a/packages/emcn/src/icons/workflow.tsx +++ b/packages/emcn/src/icons/workflow.tsx @@ -1,7 +1,7 @@ import type { SVGProps } from 'react' /** - * Workflow icon component - two nested concentric rounded squares conveying a workflow + * Workflow icon component - two nodes joined by an elbow connector * @param props - SVG properties including className, fill, etc. */ export function Workflow(props: SVGProps) { @@ -19,8 +19,9 @@ export function Workflow(props: SVGProps) { aria-hidden='true' {...props} > - - + + + ) } diff --git a/packages/emcn/src/icons/zap.tsx b/packages/emcn/src/icons/zap.tsx new file mode 100644 index 00000000000..a0758035691 --- /dev/null +++ b/packages/emcn/src/icons/zap.tsx @@ -0,0 +1,25 @@ +import type { SVGProps } from 'react' + +/** + * Zap icon component - lightning bolt + * @param props - SVG properties including className, fill, etc. + */ +export function Zap(props: SVGProps) { + return ( + + ) +} diff --git a/packages/emcn/src/index.ts b/packages/emcn/src/index.ts index 5c8509c774d..178bef24c0e 100644 --- a/packages/emcn/src/index.ts +++ b/packages/emcn/src/index.ts @@ -5,6 +5,17 @@ export * from './components' * the COMPONENT; the icon stays available from `@sim/emcn/icons`. */ export { Calendar, type CalendarProps } from './components/calendar/calendar' +/** + * `Code` exists in BOTH `./components` (the code editor) and `./icons` (a + * glyph). Same resolution as `Calendar` and `Table`: the barrel yields the + * COMPONENT, and the icon stays available from `@sim/emcn/icons`. + */ +export { + CODE_LINE_HEIGHT_PX, + Code, + calculateGutterWidth, + getCodeEditorProps, +} from './components/code/code' /** * `Table` exists in BOTH `./components` (data-table element) and `./icons` * (glyph). This explicit re-export resolves the ambiguity to the COMPONENT — diff --git a/packages/workflow-renderer/package.json b/packages/workflow-renderer/package.json index 169217aba7c..ec9d8e0ed30 100644 --- a/packages/workflow-renderer/package.json +++ b/packages/workflow-renderer/package.json @@ -28,7 +28,6 @@ "peerDependencies": { "@sim/emcn": "workspace:*", "@sim/utils": "workspace:*", - "lucide-react": ">=0.479.0", "react": "^19", "reactflow": "^11.11.4", "remark-breaks": "^4.0.0", @@ -39,7 +38,6 @@ "@sim/tsconfig": "workspace:*", "@sim/utils": "workspace:*", "@types/react": "^19", - "lucide-react": "^0.511.0", "react": "19.2.4", "reactflow": "^11.11.4", "remark-breaks": "^4.0.0", diff --git a/packages/workflow-renderer/src/edge/workflow-edge-view.tsx b/packages/workflow-renderer/src/edge/workflow-edge-view.tsx index 6256d8f70df..29c4c6e5ffc 100644 --- a/packages/workflow-renderer/src/edge/workflow-edge-view.tsx +++ b/packages/workflow-renderer/src/edge/workflow-edge-view.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react' -import { X } from 'lucide-react' +import { X } from '@sim/emcn/icons' import { BaseEdge, EdgeLabelRenderer, type EdgeProps, getSmoothStepPath } from 'reactflow' import type { EdgeDiffStatus, EdgeRunStatus } from '../types' diff --git a/packages/workflow-renderer/src/subflow/subflow-node-view.tsx b/packages/workflow-renderer/src/subflow/subflow-node-view.tsx index 335a97b4591..2c94b558013 100644 --- a/packages/workflow-renderer/src/subflow/subflow-node-view.tsx +++ b/packages/workflow-renderer/src/subflow/subflow-node-view.tsx @@ -1,6 +1,6 @@ import type { ReactNode } from 'react' import { Badge, cn, handleKeyboardActivation } from '@sim/emcn' -import { RepeatIcon, SplitIcon } from 'lucide-react' +import { Repeat, Split } from '@sim/emcn/icons' import { Handle, Position } from 'reactflow' import { HANDLE_POSITIONS } from '../dimensions' import { tileIconColorClass } from '../lib/tile-icon-color' @@ -98,7 +98,7 @@ export function SubflowNodeView({ const startHandleId = data.kind === 'loop' ? 'loop-start-source' : 'parallel-start-source' const endHandleId = data.kind === 'loop' ? 'loop-end-source' : 'parallel-end-source' - const BlockIcon = data.kind === 'loop' ? RepeatIcon : SplitIcon + const BlockIcon = data.kind === 'loop' ? Repeat : Split const blockIconBg = data.kind === 'loop' ? '#2FB3FF' : '#FEE12B' const blockName = data.name || (data.kind === 'loop' ? 'Loop' : 'Parallel') From ea94cdfa9c67b0c4acd1d1dd34e389a3e7e42c67 Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Mon, 3 Aug 2026 21:32:59 -0700 Subject: [PATCH 03/10] chore(platform): drop the dead schedule client layer and repair stale rule and skill docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up cleanup for the platform commit, which removed the workspace scheduled-tasks surface and migrated off lucide-react. Both left dead tails that type-check clean, so nothing flagged them. Six mutation hooks in hooks/queries/schedules.ts lost their only consumer when the scheduled-tasks page was deleted: useDisableSchedule, useResumeSchedule, useDeleteSchedule, useExcludeOccurrence, useUpdateSchedule, useCreateSchedule. They are removed along with the three contract objects that served only them — disableScheduleContract, excludeOccurrenceContract, deleteScheduleContract. disableScheduleBodySchema and excludeOccurrenceBodySchema are deliberately kept: both are members of scheduleUpdateSchema, the discriminated union the live PUT /api/schedules/[id] route parses. Dropping them would collapse the union and 400 the disable and exclude_occurrence actions. The schedule-calendar tree and its utils stay unmounted for later reuse. Its TSDoc now says so, since it has no importer and would otherwise read as dead code on the next sweep. The add-enrichment skill templated an import from lucide-react, a dependency the platform commit deleted, so running it produced an unresolvable import. It now points at @sim/emcn/icons, matching all five shipped enrichments. The emcn-design-review skill and several rule files still pointed at apps/sim/components/emcn/**, which moved to packages/emcn/**. Also corrects the documented Chip variant list — it advertised a ghost variant that never existed and omitted border — repoints the sim-url-state date-parser example at an inline snippet now that its source file is gone, and normalizes the one strokeWidth the icon migration left at 1.5 in bubble-chat-delay. --- .agents/skills/add-enrichment/SKILL.md | 4 +- .agents/skills/emcn-design-review/SKILL.md | 6 +- .claude/commands/add-enrichment.md | 4 +- .claude/commands/emcn-design-review.md | 6 +- .claude/rules/emcn-components.md | 2 +- .claude/rules/sim-url-state.md | 17 +- .cursor/commands/add-enrichment.md | 4 +- .cursor/commands/emcn-design-review.md | 6 +- .cursor/rules/emcn-components.mdc | 4 +- .cursor/rules/sim-styling.mdc | 2 +- .../schedule-calendar/schedule-calendar.tsx | 13 +- apps/sim/hooks/queries/schedules.ts | 226 ------------------ apps/sim/lib/api/contracts/schedules.ts | 34 +-- packages/emcn/src/AGENTS.md | 2 +- packages/emcn/src/icons/bubble-chat-delay.tsx | 2 +- scripts/README.md | 2 +- 16 files changed, 47 insertions(+), 287 deletions(-) diff --git a/.agents/skills/add-enrichment/SKILL.md b/.agents/skills/add-enrichment/SKILL.md index 7b34e4c7c38..44c3e9f95da 100644 --- a/.agents/skills/add-enrichment/SKILL.md +++ b/.agents/skills/add-enrichment/SKILL.md @@ -63,7 +63,7 @@ Why it matters: the cascade runner only bills (and only reads `output.cost.total Create `apps/sim/enrichments/{name}/{name}.ts` and a barrel `index.ts`. Mirror the existing entries (`work-email`, `phone-number`, `company-domain`, `company-info`). ```typescript -import { SomeIcon } from 'lucide-react' +import { SomeIcon } from '@sim/emcn/icons' import { filterUndefined } from '@sim/utils/object' import { normalizeDomain, splitName, str, toolProvider } from '@/enrichments/providers' import type { EnrichmentConfig } from '@/enrichments/types' @@ -109,7 +109,7 @@ export { myEnrichment } from './my-enrichment' ``` Rules: -- Keep the file **client-safe**: import only `lucide-react`, `@sim/utils/*`, `@/enrichments/providers`, and the types. **Never import `@/tools`** here — the runner does the tool call. +- Keep the file **client-safe**: import only `@sim/emcn/icons`, `@sim/utils/*`, `@/enrichments/providers`, and the types. **Never import `@/tools`** here — the runner does the tool call. - `buildParams` returns `null` when inputs are insufficient (provider skipped). `mapOutput` returns `null`/empty for a miss (falls through). Use `filterUndefined` when assembling optional tool params; coerce numbers explicitly (don't pass `''` to number outputs). - Output `id`s are the keys `mapOutput` returns; output `name`s are the default column names (the user can rename them in the config). diff --git a/.agents/skills/emcn-design-review/SKILL.md b/.agents/skills/emcn-design-review/SKILL.md index 89ae8d47843..78253e5e772 100644 --- a/.agents/skills/emcn-design-review/SKILL.md +++ b/.agents/skills/emcn-design-review/SKILL.md @@ -18,7 +18,7 @@ This codebase uses **emcn**, a custom component library built on Radix UI primit ## Steps -1. Read the emcn public barrel at `apps/sim/components/emcn/index.ts` (re-exports components, Calendar, Table*, and icons) to know what's available; for the full icon set read `apps/sim/components/emcn/icons/index.ts` +1. Read the emcn public barrel at `packages/emcn/src/index.ts` (re-exports components, Calendar, Table*, and icons) to know what's available; for the full icon set read `packages/emcn/src/icons/index.ts` 2. Read `apps/sim/app/_styles/globals.css` for CSS variable tokens 3. Analyze the specified scope against every rule below 4. If fix=true, apply the fixes. If fix=false, propose the fixes without applying. @@ -28,7 +28,7 @@ This codebase uses **emcn**, a custom component library built on Radix UI primit ## Imports - Import from `@/components/emcn` barrel, never subpaths -- Icons from `@/components/emcn/icons` or `lucide-react` +- Icons from `@sim/emcn/icons` - Use `cn` from `@/lib/core/utils/cn` for conditional classes ## Design Tokens @@ -45,7 +45,7 @@ Use CSS variable pattern (`text-[var(--text-primary)]`), never Tailwind semantic ## Buttons -Intent-to-variant mapping (read the actual `buttonVariants` in `apps/sim/components/emcn/components/button/button.tsx` for the full variant set — it exposes more than listed here): +Intent-to-variant mapping (read the actual `buttonVariants` in `packages/emcn/src/components/button/button.tsx` for the full variant set — it exposes more than listed here): | Action | Variant | |--------|---------| diff --git a/.claude/commands/add-enrichment.md b/.claude/commands/add-enrichment.md index b0beef265cf..c23d001f70a 100644 --- a/.claude/commands/add-enrichment.md +++ b/.claude/commands/add-enrichment.md @@ -62,7 +62,7 @@ Why it matters: the cascade runner only bills (and only reads `output.cost.total Create `apps/sim/enrichments/{name}/{name}.ts` and a barrel `index.ts`. Mirror the existing entries (`work-email`, `phone-number`, `company-domain`, `company-info`). ```typescript -import { SomeIcon } from 'lucide-react' +import { SomeIcon } from '@sim/emcn/icons' import { filterUndefined } from '@sim/utils/object' import { normalizeDomain, splitName, str, toolProvider } from '@/enrichments/providers' import type { EnrichmentConfig } from '@/enrichments/types' @@ -108,7 +108,7 @@ export { myEnrichment } from './my-enrichment' ``` Rules: -- Keep the file **client-safe**: import only `lucide-react`, `@sim/utils/*`, `@/enrichments/providers`, and the types. **Never import `@/tools`** here — the runner does the tool call. +- Keep the file **client-safe**: import only `@sim/emcn/icons`, `@sim/utils/*`, `@/enrichments/providers`, and the types. **Never import `@/tools`** here — the runner does the tool call. - `buildParams` returns `null` when inputs are insufficient (provider skipped). `mapOutput` returns `null`/empty for a miss (falls through). Use `filterUndefined` when assembling optional tool params; coerce numbers explicitly (don't pass `''` to number outputs). - Output `id`s are the keys `mapOutput` returns; output `name`s are the default column names (the user can rename them in the config). diff --git a/.claude/commands/emcn-design-review.md b/.claude/commands/emcn-design-review.md index 741c02c64b9..1a5c562facd 100644 --- a/.claude/commands/emcn-design-review.md +++ b/.claude/commands/emcn-design-review.md @@ -17,7 +17,7 @@ This codebase uses **emcn**, a custom component library built on Radix UI primit ## Steps -1. Read the emcn public barrel at `apps/sim/components/emcn/index.ts` (re-exports components, Calendar, Table*, and icons) to know what's available; for the full icon set read `apps/sim/components/emcn/icons/index.ts` +1. Read the emcn public barrel at `packages/emcn/src/index.ts` (re-exports components, Calendar, Table*, and icons) to know what's available; for the full icon set read `packages/emcn/src/icons/index.ts` 2. Read `apps/sim/app/_styles/globals.css` for CSS variable tokens 3. Analyze the specified scope against every rule below 4. If fix=true, apply the fixes. If fix=false, propose the fixes without applying. @@ -27,7 +27,7 @@ This codebase uses **emcn**, a custom component library built on Radix UI primit ## Imports - Import from `@/components/emcn` barrel, never subpaths -- Icons from `@/components/emcn/icons` or `lucide-react` +- Icons from `@sim/emcn/icons` - Use `cn` from `@/lib/core/utils/cn` for conditional classes ## Design Tokens @@ -44,7 +44,7 @@ Use CSS variable pattern (`text-[var(--text-primary)]`), never Tailwind semantic ## Buttons -Intent-to-variant mapping (read the actual `buttonVariants` in `apps/sim/components/emcn/components/button/button.tsx` for the full variant set — it exposes more than listed here): +Intent-to-variant mapping (read the actual `buttonVariants` in `packages/emcn/src/components/button/button.tsx` for the full variant set — it exposes more than listed here): | Action | Variant | |--------|---------| diff --git a/.claude/rules/emcn-components.md b/.claude/rules/emcn-components.md index 8b9cc4e450f..5ca3d2a8dcb 100644 --- a/.claude/rules/emcn-components.md +++ b/.claude/rules/emcn-components.md @@ -20,7 +20,7 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items ## Component catalogue -- **`Chip` / `ChipLink`** — the pill button (`