Skip to content

Commit 0371856

Browse files
authored
v0.7.17: emcn and workflow renderer isolated package, popular blocks extension, workspace forking, slack trigger extension, new README
2 parents 38c088a + 7bf8dba commit 0371856

1,128 files changed

Lines changed: 94852 additions & 14560 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/emcn-components.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ paths:
55

66
# EMCN Components
77

8-
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).
8+
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).
99

1010
## Chip chrome — single source of truth
1111

1212
Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources:
1313

14-
- **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.
14+
- **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.
1515
- **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.
1616

1717
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.

.claude/rules/sim-imports.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
4747

4848
1. React/core libraries
4949
2. External libraries
50-
3. UI components (`@/components/emcn`, `@/components/ui`)
50+
3. UI components (`@sim/emcn`, `@/components/ui`)
5151
4. Utilities (`@/lib/...`)
5252
5. Stores (`@/stores/...`)
5353
6. Feature imports

.claude/rules/sim-settings-pages.md

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ paths:
66

77
# Settings Pages
88

9-
Every settings page renders through the shared **`SettingsPanel`** primitive
10-
(`@/app/workspace/[workspaceId]/settings/components/settings-panel`). It owns the
11-
page chrome so pages never hand-roll it: a fixed header bar (right-aligned
12-
actions), a scroll region, and a centered `max-w-[48rem]` content column led by a
13-
**title + description that come from navigation metadata**. Pages render only
14-
their body.
15-
16-
Do NOT hand-roll any of these in a settings page — they are the panel's job:
9+
The Next.js `settings/[section]/layout.tsx` owns all settings page chrome via
10+
`SettingsHeaderShell` — a fixed header bar (a left back chip + right-aligned
11+
action chips), a scroll region, and a centered `max-w-[48rem]` content column led
12+
by a **title + description from navigation metadata**. The chrome stays mounted
13+
across section navigation (it never re-renders or re-lays-out). Each section
14+
renders through the **`SettingsPanel`** registrar
15+
(`@/app/workspace/[workspaceId]/settings/components/settings-panel`), which feeds
16+
the shell its header data and renders only the section body. Sections supply
17+
**data**, never chrome.
18+
19+
Do NOT hand-roll any of these in a settings page — they are owned by the layout
20+
shell (fed through `SettingsPanel`):
1721

1822
- `<div className='flex h-full flex-col bg-[var(--bg)]'>` shell
1923
- 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
2933

3034
return (
3135
<SettingsPanel
32-
actions={
33-
<Chip leftIcon={Plus} variant='primary' onClick={onCreate}>
34-
Create
35-
</Chip>
36-
}
36+
actions={[{ text: 'Create', icon: Plus, variant: 'primary', onSelect: onCreate }]}
3737
search={{ value: searchTerm, onChange: setSearchTerm, placeholder: 'Search …' }}
3838
>
3939
{/* body only — sections, lists, forms */}
@@ -54,9 +54,22 @@ return (
5454

5555
## `SettingsPanel` props
5656

57-
- `actions?: ReactNode` — right-aligned header chips. Wrap multiple in a fragment;
58-
the slot reserves the 30px chip height even when empty, so vertical rhythm is
59-
identical across pages. Conditional actions are fine: `actions={canManage && <Chip…/>}`.
57+
- `actions?: SettingsAction[]` — right-aligned header chips, **data only**:
58+
`{ text, icon?, variant?: 'primary'|'destructive', active?, onSelect, disabled?, tooltip? }`.
59+
The shell renders each as a `Chip` — never pass JSX, a `<div>`, or `className`
60+
(the locked contract: it's structurally impossible to vibe-code a padding
61+
change). Multiple/conditional actions are a plain array
62+
(`[...(canManage ? [{…}] : []), …]`). Labels are **sentence case** (`Add override`,
63+
not `Add Override`). A disabled action that needs to explain itself sets
64+
`tooltip` (the shell renders the hover tooltip, disabled chip included) — never
65+
hand-roll a tooltip-wrapped chip in `aside`. Save/Discard pairs come from the
66+
`saveDiscardActions()` helper (spread it into `actions`). Only a widget that
67+
genuinely cannot be a chip (e.g. one needing hover-prefetch) goes in `aside`.
68+
- `back?: SettingsBackAction` (`{ text, icon?, onSelect }`) — left-aligned back
69+
chip for a **detail sub-view** (e.g. a selected MCP server, a permission group,
70+
a retention policy). Detail sub-views render through `SettingsPanel` like list
71+
pages — they do NOT hand-roll their own shell.
72+
- `aside?: ReactNode` — escape hatch for the rare non-chip header widget. Keep it rare.
6073
- `search?: { value; onChange: (value: string) => void; placeholder?; disabled? }`
6174
renders the canonical search field directly below the title. Pass `setSearchTerm`
6275
straight to `onChange`. Use this for a standalone search; if search shares a row
@@ -66,8 +79,6 @@ return (
6679
detail sub-view that needs a different heading; normal pages never pass these.
6780
- `scrollContainerRef?: React.Ref<HTMLDivElement>` — forwards a ref to the scroll
6881
region (e.g. programmatic scroll-to-bottom).
69-
- `contentClassName?` — layout/spacing only; reach for it rarely. Prefer the
70-
default `gap-7`.
7182

7283
## Title + description live in navigation metadata
7384

@@ -107,12 +118,11 @@ Any settings surface with editable state uses **one** shared stack — never
107118
hand-roll a Save button, a Discard button, a `beforeunload`, or an "Unsaved
108119
changes" modal:
109120

110-
- **`SaveDiscardActions`** (`…/components/save-discard-actions/save-discard-actions`)
111-
— the canonical dirty-gated **Discard + Save** chip pair. Renders nothing when
112-
`!dirty`; otherwise a fragment so it composes beside sibling chips (a detail
113-
view's Delete / Remove override, a Share chip). Props: `dirty`, `saving`,
114-
`onSave`, `onDiscard`, `saveDisabled?`, `saveLabel?`, `savingLabel?`. Put it in
115-
the `SettingsPanel actions` slot (top-level pages) or the detail header bar.
121+
- **`saveDiscardActions(config)`** (`…/components/save-discard-actions/save-discard-actions`)
122+
— returns the canonical dirty-gated **Discard + Save** `SettingsAction[]` (empty
123+
when not dirty). Spread it into a `SettingsPanel` `actions` array, beside any
124+
sibling actions (a detail view's Delete / Remove override). Config: `dirty`,
125+
`saving`, `onSave`, `onDiscard`, `saveDisabled?`, `saveLabel?`, `savingLabel?`.
116126
- **`useSettingsUnsavedGuard({ isDirty })`** (`…/settings/hooks/use-settings-unsaved-guard`)
117127
— syncs the page's local `isDirty` into the shared `useSettingsDirtyStore` (so
118128
the sidebar's **section-switch** confirm + the centralized `beforeunload` both
@@ -141,14 +151,18 @@ changes" modal:
141151
guards real `router.push` navigation + browser Back via a history sentinel);
142152
it already shares `UnsavedChangesModal`, so copy stays unified.
143153

144-
## Detail sub-views (the one exception)
154+
## Detail sub-views
145155

146156
A drill-down view reached from a list row (selected MCP server, workflow MCP
147-
server, credential set, permission group) keeps its **own** chrome because it
148-
needs a left-aligned back button (`<Chip leftIcon={ArrowLeft}>`), which the panel
149-
header (right-actions only) does not model. Leave those returns as hand-rolled
150-
shells; only the list/main view uses `SettingsPanel`. Gate/early-return states
151-
(not-entitled, loading, upgrade prompts) also stay as-is.
157+
server, credential set, permission group, retention policy) renders through
158+
`SettingsPanel` like a list page: pass `back={{ text, icon: ArrowLeft, onSelect }}`
159+
for the left back chip, `title` (the entity name), and the header `actions`, then
160+
render the body. Do NOT hand-roll a shell or header bar; a tab bar renders as the
161+
first body child. Gate/early-return states (not-entitled, loading, upgrade
162+
prompts) stay as-is.
163+
164+
The route-based credential detail (`settings/secrets/[credentialId]`) is the lone
165+
exception — it lives outside `[section]` and keeps its own `CredentialDetailLayout`.
152166

153167
## Audit checklist
154168

.claude/rules/sim-styling.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ paths:
1616
## Conditional Classes
1717

1818
```typescript
19-
import { cn } from '@/lib/core/utils/cn'
19+
import { cn } from '@sim/emcn'
2020

2121
<div className={cn(
2222
'base-classes',
@@ -52,7 +52,7 @@ Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text
5252

5353
## Chip Components (consumer usage)
5454

55-
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@/components/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
55+
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@sim/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
5656

5757
### Props over className
5858

.cursor/rules/emcn-components.mdc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ globs: ["apps/sim/components/emcn/**"]
44
---
55
# EMCN Components
66

7-
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).
7+
Import from `@sim/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).
88

99
## Chip chrome — single source of truth
1010

1111
Never hand-roll the chip pill from raw class strings (they go stale). Compose from the canonical sources:
1212

13-
- **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.
13+
- **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.
1414
- **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.
1515

1616
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.

.cursor/rules/sim-imports.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { CORE_TRIGGER_TYPES } from '@/app/workspace/.../utils'
4646

4747
1. React/core libraries
4848
2. External libraries
49-
3. UI components (`@/components/emcn`, `@/components/ui`)
49+
3. UI components (`@sim/emcn`, `@/components/ui`)
5050
4. Utilities (`@/lib/...`)
5151
5. Stores (`@/stores/...`)
5252
6. Feature imports

.cursor/rules/sim-styling.mdc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ globs: ["apps/sim/**/*.tsx", "apps/sim/**/*.css"]
1414
## Conditional Classes
1515

1616
```typescript
17-
import { cn } from '@/lib/core/utils/cn'
17+
import { cn } from '@sim/emcn'
1818

1919
<div className={cn(
2020
'base-classes',
@@ -50,7 +50,7 @@ Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text
5050

5151
## Chip Components (consumer usage)
5252

53-
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@/components/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
53+
`ChipInput`, `ChipTextarea`, `ChipModal*` own their full chrome. Consumers describe intent through PROPS; they never re-style the chrome. The canonical chrome lives in `apps/sim/components/emcn/components/chip/chip-chrome.ts` (all tokens are re-exported from the `@sim/emcn` barrel — no subpath import needed) — never hand-roll `rounded-lg`/`border`/`bg-[var(--surface-5)]`/`h-[30px]`/`px-2`/`text-sm`/focus rings.
5454

5555
### Props over className
5656

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export f
9393

9494
1. React/core libraries
9595
2. External libraries
96-
3. UI components (`@/components/emcn`, `@/components/ui`)
96+
3. UI components (`@sim/emcn`, `@/components/ui`)
9797
4. Utilities (`@/lib/...`)
9898
5. Stores (`@/stores/...`)
9999
6. Feature imports
@@ -375,7 +375,7 @@ Co-locate a `search-params.ts` per feature exporting the parser map (single sour
375375

376376
## Styling
377377

378-
Use Tailwind only, no inline styles. Use `cn()` from `@/lib/core/utils/cn` for conditional classes.
378+
Use Tailwind only, no inline styles. Use `cn()` from `@sim/emcn` for conditional classes.
379379

380380
```typescript
381381
<div className={cn('base-classes', isActive && 'active-classes')} />
@@ -391,7 +391,7 @@ On chip components (see "EMCN Components"), drive chrome through PROPS, not `cla
391391

392392
## EMCN Components
393393

394-
Import from `@/components/emcn`, never from subpaths (except CSS files). Use CVA only when 2+ genuine variants exist; otherwise plain `cn()`.
394+
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()`.
395395

396396
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`.
397397

0 commit comments

Comments
 (0)