Skip to content

Commit 6ec0500

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Merge EMCN consolidation into public auth headings
2 parents 4491848 + 0059be1 commit 6ec0500

36 files changed

Lines changed: 681 additions & 379 deletions

File tree

‎.claude/rules/emcn-components.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items
2424
- **`Chip` / `ChipLink`** — the pill button (`<button>` / Next `<Link>`). Variants: `primary`, `destructive`, `border-shadow`, `border`, `outline` (a true `--border` border, no shadow or hover fill); the bare chip is implicit (omit `variant`). `filled` is deliberately NOT a `Chip` variant — it is reserved for chip fields/triggers. For a selected/toggle chip use the `active` prop, never a variant. `leftIcon`/`rightIcon`, `active`, `fullWidth`. Chips carry **no outer margin** — space between them is the parent's `gap`. The old `mx-0.5` default and its `flush` opt-out are gone; do not reintroduce either, and never add a margin to a chip through `className`.
2525
- **`ChipInput`** — single-line text field. `icon`, `endAdornment`, `error`, `inputClassName` (inner `<input>`); `className` styles the chrome wrapper.
2626
- **`ChipCopyInput`** — the canonical view-only field: a read-only `ChipInput` at full opacity with a trailing copy-to-clipboard button. View-only is a display mode, not a disabled state — reach for it (or `ChipModalField type='copy'`) over a `disabled` (greyed) input for values the user cannot edit.
27-
- **`ChipTextarea`** — multi-line sibling. `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
27+
- **`ChipTextarea`** — multi-line sibling. `monospace` for structured text, `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
2828
- **`ChipSelect`** — canonical menu selection. Single selection uses `value`/`onChange`; `multiSelect` uses `multiSelectValues`/`onMultiSelectChange`, checkbox rows, and stays open. Supports groups, search aliases, rich labels/icons, disabled options, optional single-selection checks, and trigger refs. Rich labels require a nonempty `searchTerms` array containing their visible text, followed by any aliases; plain string/number labels are searchable automatically. `showAllOption` clears to an empty selection; without it, use `placeholder` for the empty state. Owns its chevron. `dropdownWidth` accepts `'trigger'`, `'content'`, or pixels; `modal={false}` keeps outside-dialog filters non-modal, while nested dialogs retain their focus lock.
2929
- **`ChipCombobox`** — shared chip styling over the separate `Combobox` engine for editable values, asynchronous results, and virtualized lists. Plain menu selection, including searchable menus, uses `ChipSelect`.
30-
- **`ChipModal` + `ChipModalField`** — declarative compact modal. The field's `type` (`input` | `email` | `textarea` | `dropdown` | `copy` | `file` | `emails` | `custom`) picks the control and **owns all chrome** — consumers describe intent, never pass `variant`/`className`/`id` to the inner control. `custom` is the escape hatch. **Every body field MUST be a `ChipModalField`** — never hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). `ChipModalBody` applies `px-2` + `gap-4`; `ChipModalField` adds another `px-2`, so each field lands at effective `px-4`, exactly matching the `px-4` header/footer — a hand-rolled row skips that gutter and sits misaligned at `px-2`. For controls the field doesn't cover (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX), use `type='custom'` with a `title` — it still applies the gutter and renders the canonical `Label`.
30+
- **`ChipModal` + `ChipModalField`** — declarative compact modal. `orientation='horizontal'` places a field title beside its control, wraps on narrow surfaces, and keeps error/hint text below the row; vertical is the default. The field's `type` (`input` | `email` | `textarea` | `dropdown` | `copy` | `file` | `emails` | `custom`) picks the control and **owns all chrome** — consumers describe intent, never pass `variant`/`className`/`id` to the inner control. `custom` is the escape hatch. **Every body field MUST be a `ChipModalField`** — never hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). `ChipModalBody` applies `px-2` + `gap-4`; `ChipModalField` adds another `px-2`, so each field lands at effective `px-4`, exactly matching the `px-4` header/footer — a hand-rolled row skips that gutter and sits misaligned at `px-2`. For controls the field doesn't cover (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX), use `type='custom'` with a `title` — it still applies the gutter and renders the canonical `Label`.
3131
- **`ChipButtonGroup` / `ChipSwitch`** — one Radix radio-group implementation with zero-gap segments and `default`/`compact` density. `ChipButtonGroup` accepts compound children; `ChipSwitch` adapts an options array.
3232
- **`ChipTag`** — 20px inline tag/badge (`mono`/`gray`/`invite`), not a pill trigger.
3333
- **`ChipDatePicker`** — chip-styled date field.

‎.cursor/rules/emcn-components.mdc‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ The menu surface intentionally diverges from the pill: `dropdown-menu.tsx` items
2525
- **`Chip` / `ChipLink`** — the pill button (`<button>` / Next `<Link>`). Variants: `primary`, `destructive`, `border-shadow`, `border`, `outline` (a true `--border` border, no shadow or hover fill); the bare chip is implicit (omit `variant`). `filled` is deliberately NOT a `Chip` variant — it is reserved for chip fields/triggers. For a selected/toggle chip use the `active` prop, never a variant. `leftIcon`/`rightIcon`, `active`, `fullWidth`. Chips carry **no outer margin** — space between them is the parent's `gap`. The old `mx-0.5` default and its `flush` opt-out are gone; do not reintroduce either, and never add a margin to a chip through `className`.
2626
- **`ChipInput`** — single-line text field. `icon`, `endAdornment`, `error`, `inputClassName` (inner `<input>`); `className` styles the chrome wrapper.
2727
- **`ChipCopyInput`** — the canonical view-only field: a read-only `ChipInput` at full opacity with a trailing copy-to-clipboard button. View-only is a display mode, not a disabled state — reach for it (or `ChipModalField type='copy'`) over a `disabled` (greyed) input for values the user cannot edit.
28-
- **`ChipTextarea`** — multi-line sibling. `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
28+
- **`ChipTextarea`** — multi-line sibling. `monospace` for structured text, `error`, `resizable` (off by default), `viewOnly` (read-only at full opacity with the default cursor — the multi-line counterpart of `ChipCopyInput`).
2929
- **`ChipSelect`** — canonical menu selection. Single selection uses `value`/`onChange`; `multiSelect` uses `multiSelectValues`/`onMultiSelectChange`, checkbox rows, and stays open. Supports groups, search aliases, rich labels/icons, disabled options, optional single-selection checks, and trigger refs. Rich labels require a nonempty `searchTerms` array containing their visible text, followed by any aliases; plain string/number labels are searchable automatically. `showAllOption` clears to an empty selection; without it, use `placeholder` for the empty state. Owns its chevron. `dropdownWidth` accepts `'trigger'`, `'content'`, or pixels; `modal={false}` keeps outside-dialog filters non-modal, while nested dialogs retain their focus lock.
3030
- **`ChipCombobox`** — shared chip styling over the separate `Combobox` engine for editable values, asynchronous results, and virtualized lists. Plain menu selection, including searchable menus, uses `ChipSelect`.
31-
- **`ChipModal` + `ChipModalField`** — declarative compact modal. The field's `type` (`input` | `email` | `textarea` | `dropdown` | `copy` | `file` | `emails` | `custom`) picks the control and **owns all chrome** — consumers describe intent, never pass `variant`/`className`/`id` to the inner control. `custom` is the escape hatch. **Every body field MUST be a `ChipModalField`** — never hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). `ChipModalBody` applies `px-2` + `gap-4`; `ChipModalField` adds another `px-2`, so each field lands at effective `px-4`, exactly matching the `px-4` header/footer — a hand-rolled row skips that gutter and sits misaligned at `px-2`. For controls the field doesn't cover (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX), use `type='custom'` with a `title` — it still applies the gutter and renders the canonical `Label`.
31+
- **`ChipModal` + `ChipModalField`** — declarative compact modal. `orientation='horizontal'` places a field title beside its control, wraps on narrow surfaces, and keeps error/hint text below the row; vertical is the default. The field's `type` (`input` | `email` | `textarea` | `dropdown` | `copy` | `file` | `emails` | `custom`) picks the control and **owns all chrome** — consumers describe intent, never pass `variant`/`className`/`id` to the inner control. `custom` is the escape hatch. **Every body field MUST be a `ChipModalField`** — never hand-roll a field row (raw `<div>` + hand-rolled `<p>`/`<label>` title + bare `ChipInput`/`ChipTextarea`). `ChipModalBody` applies `px-2` + `gap-4`; `ChipModalField` adds another `px-2`, so each field lands at effective `px-4`, exactly matching the `px-4` header/footer — a hand-rolled row skips that gutter and sits misaligned at `px-2`. For controls the field doesn't cover (`ChipCombobox`, `ChipSelect`, `DatePicker`, `TimePicker`, `ButtonGroup`, arbitrary JSX), use `type='custom'` with a `title` — it still applies the gutter and renders the canonical `Label`.
3232
- **`ChipButtonGroup` / `ChipSwitch`** — one Radix radio-group implementation with zero-gap segments and `default`/`compact` density. `ChipButtonGroup` accepts compound children; `ChipSwitch` adapts an options array.
3333
- **`ChipTag`** — 20px inline tag/badge (`mono`/`gray`/`invite`), not a pill trigger.
3434
- **`ChipDatePicker`** — chip-styled date field.

‎apps/sim/app/(interfaces)/chat/components/error-state/error-state.tsx‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { Button, StatusPageContent } from '@sim/emcn'
3+
import { Chip, StatusPageContent } from '@sim/emcn'
44
import { useRouter } from 'next/navigation'
55
import { APP_ENTRY_PATH } from '@/lib/navigation/paths'
66

@@ -14,14 +14,15 @@ export function ChatErrorState({ error }: ChatErrorStateProps) {
1414
return (
1515
<div className='flex flex-1 items-center justify-center px-4 py-16 text-center'>
1616
<StatusPageContent title='Chat Unavailable' description={error}>
17-
<Button
18-
type='button'
17+
<Chip
1918
variant='primary'
20-
className='h-[32px] w-full gap-2 px-2.5 text-sm'
2119
onClick={() => router.push(APP_ENTRY_PATH)}
20+
fullWidth
21+
type='button'
22+
align='center'
2223
>
2324
Open Sim
24-
</Button>
25+
</Chip>
2526
</StatusPageContent>
2627
</div>
2728
)

‎apps/sim/app/(interfaces)/resume/[workflowId]/[executionId]/resume-page-client.tsx‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -783,17 +783,18 @@ export default function ResumeExecutionPage({
783783
</div>
784784
<Tooltip.Root>
785785
<Tooltip.Trigger asChild>
786-
<Button
786+
<Chip
787787
variant='outline'
788-
size='sm'
788+
type='button'
789789
onClick={handleRefreshExecution}
790790
disabled={refreshingExecution}
791-
className='gap-1.5 px-2.5'
791+
leftAdornment={
792+
<RefreshCw className={cn('size-[14px]', refreshingExecution && 'animate-spin')} />
793+
}
792794
aria-label='Refresh execution details'
793795
>
794-
<RefreshCw className={cn('size-[14px]', refreshingExecution && 'animate-spin')} />
795796
Refresh
796-
</Button>
797+
</Chip>
797798
</Tooltip.Trigger>
798799
<Tooltip.Content>Refresh</Tooltip.Content>
799800
</Tooltip.Root>
@@ -1022,7 +1023,8 @@ export default function ResumeExecutionPage({
10221023
placeholder='{"example": "value"}'
10231024
rows={6}
10241025
spellCheck={false}
1025-
className='min-h-[180px] font-mono'
1026+
monospace
1027+
className='min-h-[180px]'
10261028
/>
10271029
</div>
10281030
</div>

‎apps/sim/app/workspace/[workspaceId]/components/resource/resource.tsx‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,10 @@ const Pagination = memo(function Pagination({
536536
<Button
537537
key={page}
538538
type='button'
539-
variant='ghost'
539+
variant={page === currentPage ? 'subtle' : 'ghost'}
540+
size='inline'
541+
aria-current={page === currentPage ? 'page' : undefined}
540542
onClick={() => onPageChange(page)}
541-
className={cn(
542-
'h-auto p-0 text-sm hover-hover:bg-transparent hover-hover:text-[var(--text-body)]',
543-
page === currentPage ? 'text-[var(--text-body)]' : ''
544-
)}
545543
>
546544
{page}
547545
</Button>

‎apps/sim/app/workspace/[workspaceId]/files/files.tsx‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
44
import {
5-
Button,
5+
Chip,
66
ChipCombobox,
77
ChipConfirmModal,
88
Columns2,
@@ -2057,13 +2057,9 @@ function FilesContent() {
20572057
</div>
20582058
)}
20592059
{hasActiveFilters && (
2060-
<Button
2061-
variant='ghost'
2062-
onClick={clearFileFilters}
2063-
className='h-[32px] w-full hover-hover:bg-[var(--surface-active)]'
2064-
>
2060+
<Chip fullWidth onClick={clearFileFilters} align='center'>
20652061
Clear all filters
2066-
</Button>
2062+
</Chip>
20672063
)}
20682064
</div>
20692065
)

‎apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/client-credential-account-modal.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ function ClientCredentialAccountModalForm({
275275
value={value}
276276
onChange={(event) => setField(field.id, event.target.value)}
277277
placeholder={field.placeholder}
278-
className='min-h-[120px] font-mono'
278+
monospace
279+
className='min-h-[120px]'
279280
// Browser spell-check and autofill ship textarea contents to
280281
// third-party services — an exfiltration route for a pasted
281282
// private key. `ChipModalField type='textarea'` exposes none

‎apps/sim/app/workspace/[workspaceId]/knowledge/components/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { KnowledgeFilterHeading } from '@/app/workspace/[workspaceId]/knowledge/components/knowledge-filter-heading'
12
export { CreateBaseModal } from './create-base-modal'
23
export { DeleteKnowledgeBaseModal } from './delete-knowledge-base-modal'
34
export { EditKnowledgeBaseModal } from './edit-knowledge-base-modal'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Button } from '@sim/emcn'
2+
import { FILTER_SECTION_LABEL_CLASS } from '@/app/workspace/[workspaceId]/components'
3+
4+
interface KnowledgeFilterHeadingProps {
5+
title: string
6+
active: boolean
7+
onClear: () => void
8+
}
9+
10+
/** Heading and conditional clear action for a knowledge-list filter. */
11+
export function KnowledgeFilterHeading({ title, active, onClear }: KnowledgeFilterHeadingProps) {
12+
return (
13+
<div className='flex h-5 items-center justify-between'>
14+
<span className={FILTER_SECTION_LABEL_CLASS}>{title}</span>
15+
{active && (
16+
<Button variant='ghost-secondary' size='inline' onClick={onClear} className='-mr-1'>
17+
Clear
18+
</Button>
19+
)}
20+
</div>
21+
)
22+
}

‎apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.test.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ vi.mock('@/connectors/registry', () => ({ CONNECTOR_META_REGISTRY: {} }))
9696
vi.mock('@/app/workspace/[workspaceId]/knowledge/[id]/components/base-tags-modal', () => ({
9797
BaseTagsModal: () => null,
9898
}))
99-
vi.mock('@/app/workspace/[workspaceId]/knowledge/components', () => ({
99+
vi.mock('@/app/workspace/[workspaceId]/knowledge/components', async () => ({
100+
...(await import('@/app/workspace/[workspaceId]/knowledge/components/knowledge-filter-heading')),
100101
CreateBaseModal: () => null,
101102
EditKnowledgeBaseModal: () => null,
102103
KnowledgeListContextMenu: () => null,

0 commit comments

Comments
 (0)