diff --git a/.claude/rules/emcn-components.md b/.claude/rules/emcn-components.md index 9e096c2a9bd..23491d6aaaf 100644 --- a/.claude/rules/emcn-components.md +++ b/.claude/rules/emcn-components.md @@ -5,13 +5,13 @@ paths: # EMCN Components -Import from `@/components/emcn`, never from subpaths (except CSS files). The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch`→`ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover). +Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. The **chip family** is the platform's primary chrome — always reach for it over the legacy primitives it is progressively replacing (`Input`→`ChipInput`, `Textarea`→`ChipTextarea`, `Modal`→`ChipModal`, `Select`/`Combobox`→`ChipSelect`/`ChipCombobox`/`ChipDropdown`, `Switch`→`ChipSwitch`, date field→`ChipDatePicker`). For context/action menus the canonical control is `DropdownMenu` — the standard menu (not a chip, and never a hand-rolled popover). ## Chip chrome — single source of truth Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources: -- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, and `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells). All are re-exported from the `@/components/emcn` barrel — no subpath import needed. +- **Surface, typography + content tokens:** `chip/chip-chrome.ts` — `chipFilledSurfaceTokens`, `chipFieldSurfaceClass`, `chipFieldTextClass` (text fields and the dropdown search box build on these), plus the chip-content chrome `chipContentGap`, `chipGeometryClass`, `chipContentIconClass`, `chipContentLabelClass`, and `cellIconNodeClass` (non-chip surfaces that must visually match chip content, e.g. resource table cells). All are re-exported from the `@sim/emcn` barrel — no subpath import needed. - **Pill geometry:** `chip/chip.tsx` — `chipVariants` (30px tall, `rounded-lg`, `px-2`, icon↔text `gap-1.5`). Every pill-shaped trigger (`ChipDropdown`, `ChipSelect`, `ChipSwitch`) reuses it for visual parity. Canonical look: normal font-weight (never `font-medium`/`font-semibold`), value text `--text-body`, icons `--text-icon` at `size-[14px]`, placeholder `--text-muted`, `transition-colors`, **no focus ring** (the caret marks focus). Filled surface is `--surface-5` light / `--surface-4` dark with a `--border-1` border. diff --git a/.claude/rules/sim-imports.md b/.claude/rules/sim-imports.md index b1f1926cd9d..74bf556db4d 100644 --- a/.claude/rules/sim-imports.md +++ b/.claude/rules/sim-imports.md @@ -47,7 +47,7 @@ import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils' 1. React/core libraries 2. External libraries -3. UI components (`@/components/emcn`, `@/components/ui`) +3. UI components (`@sim/emcn`, `@/components/ui`) 4. Utilities (`@/lib/...`) 5. Stores (`@/stores/...`) 6. Feature imports diff --git a/.claude/rules/sim-settings-pages.md b/.claude/rules/sim-settings-pages.md index 1f666fc59f2..f2e7a5f63b5 100644 --- a/.claude/rules/sim-settings-pages.md +++ b/.claude/rules/sim-settings-pages.md @@ -6,14 +6,18 @@ paths: # Settings Pages -Every settings page renders through the shared **`SettingsPanel`** primitive -(`@/app/workspace/[workspaceId]/settings/components/settings-panel`). It owns the -page chrome so pages never hand-roll it: a fixed header bar (right-aligned -actions), a scroll region, and a centered `max-w-[48rem]` content column led by a -**title + description that come from navigation metadata**. Pages render only -their body. - -Do NOT hand-roll any of these in a settings page — they are the panel's job: +The Next.js `settings/[section]/layout.tsx` owns all settings page chrome via +`SettingsHeaderShell` — a fixed header bar (a left back chip + right-aligned +action chips), a scroll region, and a centered `max-w-[48rem]` content column led +by a **title + description from navigation metadata**. The chrome stays mounted +across section navigation (it never re-renders or re-lays-out). Each section +renders through the **`SettingsPanel`** registrar +(`@/app/workspace/[workspaceId]/settings/components/settings-panel`), which feeds +the shell its header data and renders only the section body. Sections supply +**data**, never chrome. + +Do NOT hand-roll any of these in a settings page — they are owned by the layout +shell (fed through `SettingsPanel`): - `
` shell - the header bar (`flex flex-shrink-0 … px-[16px] pt-[8.5px] pb-[8.5px]`) @@ -29,11 +33,7 @@ import { SettingsPanel } from '@/app/workspace/[workspaceId]/settings/components return ( - Create - - } + actions={[{ text: 'Create', icon: Plus, variant: 'primary', onSelect: onCreate }]} search={{ value: searchTerm, onChange: setSearchTerm, placeholder: 'Search …' }} > {/* body only — sections, lists, forms */} @@ -54,9 +54,22 @@ return ( ## `SettingsPanel` props -- `actions?: ReactNode` — right-aligned header chips. Wrap multiple in a fragment; - the slot reserves the 30px chip height even when empty, so vertical rhythm is - identical across pages. Conditional actions are fine: `actions={canManage && }`. +- `actions?: SettingsAction[]` — right-aligned header chips, **data only**: + `{ text, icon?, variant?: 'primary'|'destructive', active?, onSelect, disabled?, tooltip? }`. + The shell renders each as a `Chip` — never pass JSX, a `
`, or `className` + (the locked contract: it's structurally impossible to vibe-code a padding + change). Multiple/conditional actions are a plain array + (`[...(canManage ? [{…}] : []), …]`). Labels are **sentence case** (`Add override`, + not `Add Override`). A disabled action that needs to explain itself sets + `tooltip` (the shell renders the hover tooltip, disabled chip included) — never + hand-roll a tooltip-wrapped chip in `aside`. Save/Discard pairs come from the + `saveDiscardActions()` helper (spread it into `actions`). Only a widget that + genuinely cannot be a chip (e.g. one needing hover-prefetch) goes in `aside`. +- `back?: SettingsBackAction` (`{ text, icon?, onSelect }`) — left-aligned back + chip for a **detail sub-view** (e.g. a selected MCP server, a permission group, + a retention policy). Detail sub-views render through `SettingsPanel` like list + pages — they do NOT hand-roll their own shell. +- `aside?: ReactNode` — escape hatch for the rare non-chip header widget. Keep it rare. - `search?: { value; onChange: (value: string) => void; placeholder?; disabled? }` — renders the canonical search field directly below the title. Pass `setSearchTerm` straight to `onChange`. Use this for a standalone search; if search shares a row @@ -66,8 +79,6 @@ return ( detail sub-view that needs a different heading; normal pages never pass these. - `scrollContainerRef?: React.Ref` — forwards a ref to the scroll region (e.g. programmatic scroll-to-bottom). -- `contentClassName?` — layout/spacing only; reach for it rarely. Prefer the - default `gap-7`. ## Title + description live in navigation metadata @@ -107,12 +118,11 @@ Any settings surface with editable state uses **one** shared stack — never hand-roll a Save button, a Discard button, a `beforeunload`, or an "Unsaved changes" modal: -- **`SaveDiscardActions`** (`…/components/save-discard-actions/save-discard-actions`) - — the canonical dirty-gated **Discard + Save** chip pair. Renders nothing when - `!dirty`; otherwise a fragment so it composes beside sibling chips (a detail - view's Delete / Remove override, a Share chip). Props: `dirty`, `saving`, - `onSave`, `onDiscard`, `saveDisabled?`, `saveLabel?`, `savingLabel?`. Put it in - the `SettingsPanel actions` slot (top-level pages) or the detail header bar. +- **`saveDiscardActions(config)`** (`…/components/save-discard-actions/save-discard-actions`) + — returns the canonical dirty-gated **Discard + Save** `SettingsAction[]` (empty + when not dirty). Spread it into a `SettingsPanel` `actions` array, beside any + sibling actions (a detail view's Delete / Remove override). Config: `dirty`, + `saving`, `onSave`, `onDiscard`, `saveDisabled?`, `saveLabel?`, `savingLabel?`. - **`useSettingsUnsavedGuard({ isDirty })`** (`…/settings/hooks/use-settings-unsaved-guard`) — syncs the page's local `isDirty` into the shared `useSettingsDirtyStore` (so the sidebar's **section-switch** confirm + the centralized `beforeunload` both @@ -141,14 +151,18 @@ changes" modal: guards real `router.push` navigation + browser Back via a history sentinel); it already shares `UnsavedChangesModal`, so copy stays unified. -## Detail sub-views (the one exception) +## Detail sub-views A drill-down view reached from a list row (selected MCP server, workflow MCP -server, credential set, permission group) keeps its **own** chrome because it -needs a left-aligned back button (``), which the panel -header (right-actions only) does not model. Leave those returns as hand-rolled -shells; only the list/main view uses `SettingsPanel`. Gate/early-return states -(not-entitled, loading, upgrade prompts) also stay as-is. +server, credential set, permission group, retention policy) renders through +`SettingsPanel` like a list page: pass `back={{ text, icon: ArrowLeft, onSelect }}` +for the left back chip, `title` (the entity name), and the header `actions`, then +render the body. Do NOT hand-roll a shell or header bar; a tab bar renders as the +first body child. Gate/early-return states (not-entitled, loading, upgrade +prompts) stay as-is. + +The route-based credential detail (`settings/secrets/[credentialId]`) is the lone +exception — it lives outside `[section]` and keeps its own `CredentialDetailLayout`. ## Audit checklist diff --git a/.claude/rules/sim-styling.md b/.claude/rules/sim-styling.md index 61e4c5d1967..3f926b78828 100644 --- a/.claude/rules/sim-styling.md +++ b/.claude/rules/sim-styling.md @@ -16,7 +16,7 @@ paths: ## Conditional Classes ```typescript -import { cn } from '@/lib/core/utils/cn' +import { cn } from '@sim/emcn'
@@ -391,7 +391,7 @@ On chip components (see "EMCN Components"), drive chrome through PROPS, not `cla ## EMCN Components -Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`. +Import components, `cn`, and tokens from the `@sim/emcn` barrel; icons come from the `@sim/emcn/icons` subpath, and CSS modules from their file path. Never deep-import other component subpaths. Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`. The chip family is the canonical UI chrome and is progressively replacing the legacy EMCN primitives — always reach for the chip equivalent: `ChipInput` over `Input`, `ChipTextarea` over `Textarea`, `ChipModal`/`ChipModalField` over `Modal`, `ChipSelect`/`ChipCombobox` (searchable) or `ChipDropdown` (simple menu-select) over `Select`/`Combobox`, `ChipSwitch` over `Switch`, `ChipDatePicker` over a raw date field, `Chip`/`ChipLink` for pill buttons/links, `ChipTag` for inline tags/badges. For context/action menus the canonical control is `DropdownMenu` (not a chip, but the standard menu — not a hand-rolled popover). Components OWN their chrome (single source of truth) — consumers pass props, not class overrides. Authoring rules in `.claude/rules/emcn-components.md`; consumer rules in `.claude/rules/sim-styling.md`. diff --git a/README.md b/README.md index 90246943df1..88dc99f7c1f 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,54 @@

- - - - - Sim Logo - - -

- -

The open-source AI workspace where teams build, deploy, and manage AI agents. Build conversationally, visually, or with code. Connect 1,000+ integrations and every major LLM to automate real work.

- -

- Sim.ai - Discord - Twitter - Documentation -

- -

- Ask DeepWiki Set Up with Cursor -

- -### Build everything in Chat -Your AI command center. Describe what you want in plain language. Sim knows your entire workspace and takes action: building agents, running them, querying data, and more. - -

- Sim building and running an agent from chat -

- -### Create files and documents -Generate documents, reports, and presentations from a single prompt, grounded in your workspace data. - -

- Sim generating a document from a prompt + Sim.ai + Documentation + Discord + X

-### Ground agents in your knowledge -Upload documents to a knowledge base and let agents answer questions from your own content. -

- Creating a knowledge base + Ask DeepWiki + Set Up with Cursor

-### Structured data with Tables -A database, built in. Store, query, and wire structured data into agent runs. -

- Tables view with typed columns + + Sim — your workflow agent for solving automations. Build, deploy, and manage AI agents visually, conversationally, or with code. +

-### Build visually with Workflows -Prefer a canvas? Design agents block by block in the visual builder, and let Sim generate blocks, wire variables, and fix errors from natural language. - -

- Workflow builder demo -

+

A workspace to build, deploy and manage AI agents and workflows.

## Quickstart ### Cloud-hosted: [sim.ai](https://sim.ai) -Sim.ai +Open sim.ai -### Self-hosted: NPM Package +### Self-hosted ```bash npx simstudio ``` -→ http://localhost:3000 -#### Note -Docker must be installed and running on your machine. +Open [http://localhost:3000](http://localhost:3000) + +Docker must be installed and running. Use `-p, --port ` to run Sim on a different port, or `--no-pull` to skip pulling the latest Docker images. + +

+ How Sim works — Integrate, Context, Build, Monitor — shown end to end: start a chat to build an agent, connect Slack and other integrations, add a knowledge base, build the workflow visually, deploy it, and monitor runs in the logs +

+ +## Capabilities -#### Options +- Connect 1,000+ integrations and every major LLM +- Add Slack, Notion, HubSpot, Salesforce, databases, and more +- Build agents visually, conversationally, or with code +- Ingest files, knowledge bases, and structured table data +- Monitor runs, logs, schedules, and workflow activity -| Flag | Description | -|------|-------------| -| `-p, --port ` | Port to run Sim on (default `3000`) | -| `--no-pull` | Skip pulling latest Docker images | +## Self-hosting -### Self-hosted: Docker Compose +### Docker Compose ```bash git clone https://github.com/simstudioai/sim.git && cd sim @@ -90,7 +59,7 @@ Open [http://localhost:3000](http://localhost:3000) Sim also supports local models via [Ollama](https://ollama.ai) and [vLLM](https://docs.vllm.ai/). See the [Docker self-hosting docs](https://docs.sim.ai/self-hosting/docker) for setup details. -### Self-hosted: Manual Setup +### Manual Setup **Requirements:** [Bun](https://bun.sh/), [Node.js](https://nodejs.org/) v20+, PostgreSQL 12+ with [pgvector](https://github.com/pgvector/pgvector) @@ -175,4 +144,6 @@ We welcome contributions! Please see our [Contributing Guide](.github/CONTRIBUTI This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. -

Made with ❤️ by the Sim Team

+

+ Built by the Sim team in San Francisco +

diff --git a/apps/docs/app/[lang]/not-found.tsx b/apps/docs/app/[lang]/not-found.tsx index af647ec4cf2..c0c60878855 100644 --- a/apps/docs/app/[lang]/not-found.tsx +++ b/apps/docs/app/[lang]/not-found.tsx @@ -1,5 +1,5 @@ +import { ChipLink } from '@sim/emcn' import { DocsPage } from 'fumadocs-ui/page' -import { ChipLink } from '@/components/ui/chip' export const metadata = { title: 'Page Not Found', diff --git a/apps/docs/app/global.css b/apps/docs/app/global.css index 2c24e2867eb..cbfd2ef2639 100644 --- a/apps/docs/app/global.css +++ b/apps/docs/app/global.css @@ -2,6 +2,8 @@ @import "fumadocs-ui/css/neutral.css"; @import "fumadocs-ui/css/preset.css"; @import "fumadocs-openapi/css/preset.css"; +@source "../../../packages/emcn/src"; +@source "../../../packages/workflow-renderer/src"; /* Prevent overscroll bounce effect on the page */ html, @@ -18,6 +20,20 @@ body { @theme { --color-fd-primary: var(--color-fd-foreground); + /* Sim's custom type scale — emcn components (Label, Badge, the shared block + views) use these names, so they must resolve here or text falls back to the + inherited size. Mirrors apps/sim/tailwind.config.ts. */ + --text-micro: 10px; + --text-xs: 11px; + --text-caption: 12px; + --text-small: 13px; + --text-base: 15px; + --text-md: 16px; +} + +/* Hide the floating Ask Sim widget while a workflow-preview lightbox is open. */ +body.wp-lightbox-open [aria-label="Ask Sim"] { + display: none; } /* Pure white light mode background */ @@ -41,6 +57,7 @@ body { */ :root { --bg: #fefefe; + --workflow-edge: #e0e0e0; --surface-1: #fbfbfb; --surface-2: #ffffff; --surface-3: #f7f7f7; @@ -50,6 +67,7 @@ body { --surface-active: #ececec; --border: #dedede; --border-1: #e0e0e0; + --divider: #ededed; --text-primary: #1a1a1a; --text-secondary: #525252; --text-tertiary: #5c5c5c; @@ -59,8 +77,14 @@ body { --text-icon: #525252; --text-inverse: #ffffff; --text-error: #ef4444; + --text-muted-inverse: #a0a0a0; --brand-accent: #33c482; --brand-accent-hover: #2dac72; + --brand-secondary: #33b4ff; + --surface-inverted: #1b1b1b; + --surface-inverted-hover: #363636; + --border-inverted: #363636; + --border-muted: #e4e4e4; --badge-success-bg: #bbf7d0; --badge-success-text: #15803d; --badge-blue-bg: #bfdbfe; @@ -84,6 +108,7 @@ body { .dark { --bg: #1b1b1b; + --workflow-edge: #454545; --surface-1: #1e1e1e; --surface-2: #232323; --surface-3: #242424; @@ -93,6 +118,7 @@ body { --surface-active: #2c2c2c; --border: #333333; --border-1: #3d3d3d; + --divider: #393939; --text-primary: #e6e6e6; --text-secondary: #cccccc; --text-tertiary: #b3b3b3; @@ -102,8 +128,14 @@ body { --text-icon: #a0a0a0; --text-inverse: #1b1b1b; --text-error: #ef4444; + --text-muted-inverse: #b3b3b3; --brand-accent: #33c482; --brand-accent-hover: #2dac72; + --brand-secondary: #33b4ff; + --surface-inverted: #242424; + --surface-inverted-hover: #363636; + --border-inverted: #3d3d3d; + --border-muted: #424242; --badge-success-bg: rgba(34, 197, 94, 0.2); --badge-success-text: #86efac; --badge-blue-bg: rgba(59, 130, 246, 0.2); @@ -1648,56 +1680,6 @@ main article blockquote { box-shadow: none !important; } -/* Workflow-preview theme scope. Values mirror the app's tokens in - apps/sim/app/_styles/globals.css (light from :root/.light, dark from .dark) - so the docs previews match the OG repository in both modes. */ -.wp-scope { - /* surfaces */ - --wp-canvas: var(--bg); - --wp-panel: var(--surface-1); - --wp-surface: var(--surface-2); - --wp-header: var(--surface-3); - --wp-btn: var(--surface-4); - --wp-control: var(--surface-5); - --wp-active: var(--surface-active); - --wp-container-fill: rgba(0, 0, 0, 0.02); - - /* borders */ - --wp-border: var(--border); - --wp-border-1: var(--border-1); - --wp-chip-bg: var(--surface-5); /* ChipTag surface (light) */ - --wp-chip-text: var(--text-body); - --wp-divider: #ededed; /* --divider */ - --wp-edge: #e0e0e0; /* --workflow-edge */ - --wp-highlight: #33b4ff; /* traced edge / highlighted output node */ - - /* text */ - --wp-text: var(--text-primary); - --wp-text-2: var(--text-secondary); - --wp-text-3: var(--text-tertiary); - --wp-text-muted: var(--text-muted); - --wp-text-subtle: var(--text-subtle); - - /* type badges (output inspector) */ - --wp-badge-success-bg: var(--badge-success-bg); - --wp-badge-success-text: var(--badge-success-text); - --wp-badge-blue-bg: var(--badge-blue-bg); - --wp-badge-blue-text: var(--badge-blue-text); - --wp-badge-orange-bg: var(--badge-orange-bg); - --wp-badge-orange-text: var(--badge-orange-text); - --wp-badge-purple-bg: var(--badge-purple-bg); - --wp-badge-purple-text: var(--badge-purple-text); - --wp-badge-gray-bg: var(--badge-gray-bg); - --wp-badge-gray-text: var(--badge-gray-text); -} - -.dark .wp-scope { - --wp-container-fill: rgba(255, 255, 255, 0.02); - --wp-chip-bg: var(--surface-4); /* ChipTag surface (dark) */ - --wp-divider: #393939; - --wp-edge: #454545; -} - /* Tailwind v4 content sources */ @source '../app/**/*.{js,ts,jsx,tsx,mdx}'; @source '../components/**/*.{js,ts,jsx,tsx,mdx}'; diff --git a/apps/docs/components/ai/ask-ai.tsx b/apps/docs/components/ai/ask-ai.tsx index dca3886305f..e0dc64604f7 100644 --- a/apps/docs/components/ai/ask-ai.tsx +++ b/apps/docs/components/ai/ask-ai.tsx @@ -82,12 +82,12 @@ export function AskAI({ locale }: AskAIProps) { {!open && ( )} @@ -96,7 +96,7 @@ export function AskAI({ locale }: AskAIProps) {
- Ask AI + Ask Sim