mirror of
https://github.com/simstudioai/sim.git
synced 2026-08-29 02:27:35 +08:00
improvement(emcn): extract design system into shared @sim/emcn package (#5257)
Moves apps/sim/components/emcn into a shared @sim/emcn package consumed directly by apps/sim and apps/docs. cn/keyboard/use-copy-to-clipboard move into the package; all imports become direct @sim/emcn (icons via @sim/emcn/icons, CSS via file path). ChipModal email validation is now prop-driven (quickValidateEmail stays in apps/sim, injected via validate). Docs drops its local chip/chip-dropdown/dropdown-menu mirrors and consumes @sim/emcn.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -16,7 +16,7 @@ paths:
|
||||
## Conditional Classes
|
||||
|
||||
```typescript
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { cn } from '@sim/emcn'
|
||||
|
||||
<div className={cn(
|
||||
'base-classes',
|
||||
@@ -52,7 +52,7 @@ Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text
|
||||
|
||||
## Chip Components (consumer usage)
|
||||
|
||||
`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.
|
||||
`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.
|
||||
|
||||
### Props over className
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ globs: ["apps/sim/components/emcn/**"]
|
||||
---
|
||||
# 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 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).
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -46,7 +46,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
|
||||
|
||||
@@ -14,7 +14,7 @@ globs: ["apps/sim/**/*.tsx", "apps/sim/**/*.css"]
|
||||
## Conditional Classes
|
||||
|
||||
```typescript
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { cn } from '@sim/emcn'
|
||||
|
||||
<div className={cn(
|
||||
'base-classes',
|
||||
@@ -50,7 +50,7 @@ Value text `--text-body`; muted/placeholder/labels `--text-muted`; icons `--text
|
||||
|
||||
## Chip Components (consumer usage)
|
||||
|
||||
`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.
|
||||
`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.
|
||||
|
||||
### Props over className
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ Use barrel exports (`index.ts`) when a folder has 3+ exports. Do not re-export f
|
||||
|
||||
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
|
||||
@@ -375,7 +375,7 @@ Co-locate a `search-params.ts` per feature exporting the parser map (single sour
|
||||
|
||||
## Styling
|
||||
|
||||
Use Tailwind only, no inline styles. Use `cn()` from `@/lib/core/utils/cn` for conditional classes.
|
||||
Use Tailwind only, no inline styles. Use `cn()` from `@sim/emcn` for conditional classes.
|
||||
|
||||
```typescript
|
||||
<div className={cn('base-classes', isActive && 'active-classes')} />
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -59,8 +59,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;
|
||||
@@ -102,8 +108,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);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { ChipLink } from '@sim/emcn'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { ChipLink } from '@/components/ui/chip'
|
||||
import { LanguageDropdown } from '@/components/ui/language-dropdown'
|
||||
import { SearchTrigger } from '@/components/ui/search-trigger'
|
||||
import { SimLogoFull } from '@/components/ui/sim-logo'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { Chip } from '@sim/emcn'
|
||||
import { useCopyButton } from 'fumadocs-ui/utils/use-copy-button'
|
||||
import { Check, Copy } from 'lucide-react'
|
||||
import { Chip } from '@/components/ui/chip'
|
||||
|
||||
export function LLMCopyButton({ content }: { content: string }) {
|
||||
const [checked, onClick] = useCopyButton(() => navigator.clipboard.writeText(content))
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { type ComponentType, forwardRef, type ReactNode } from 'react'
|
||||
import { Check } from 'lucide-react'
|
||||
import { ChipChevronDown, chipVariants, TRIGGER_BORDER_CLASS } from '@/components/ui/chip'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
type ChipIcon = ComponentType<{ className?: string }>
|
||||
|
||||
/**
|
||||
* Single option rendered inside a {@link ChipDropdown}.
|
||||
*/
|
||||
interface ChipDropdownOption {
|
||||
/** Stable identifier returned via `onChange`. */
|
||||
value: string
|
||||
/** Visual label rendered both inside the trigger (when selected) and in the menu. */
|
||||
label: ReactNode
|
||||
/** Optional leading icon rendered inside the menu item. */
|
||||
icon?: ChipIcon
|
||||
/** Pre-rendered leading element (e.g. a flag) — takes precedence over `icon`. */
|
||||
iconElement?: ReactNode
|
||||
}
|
||||
|
||||
interface ChipDropdownProps {
|
||||
/** Options to render in the menu. */
|
||||
options: ReadonlyArray<ChipDropdownOption>
|
||||
/** Currently selected value. */
|
||||
value?: string
|
||||
/** Called when the user picks a different option from the menu. */
|
||||
onChange?: (value: string) => void
|
||||
/** Shown in the trigger when nothing is selected. */
|
||||
placeholder?: string
|
||||
/** Aligns the dropdown popover relative to the trigger. */
|
||||
align?: 'start' | 'center' | 'end'
|
||||
/**
|
||||
* Whether the menu width should match the trigger's. When `true` (default),
|
||||
* the menu pins to `--radix-dropdown-menu-trigger-width`. Set `false` to let
|
||||
* the menu size to its widest item.
|
||||
*/
|
||||
matchTriggerWidth?: boolean
|
||||
/** Forwarded to the inner `DropdownMenuContent`. */
|
||||
contentClassName?: string
|
||||
/** Disables the trigger. */
|
||||
disabled?: boolean
|
||||
/** Optional icon rendered before the label. */
|
||||
leftIcon?: ChipIcon
|
||||
/** Forwarded class for the trigger button — layout/sizing only. */
|
||||
className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Docs-local mirror of the emcn `ChipDropdown`
|
||||
* (`apps/sim/components/emcn/components/chip-dropdown/chip-dropdown.tsx`) — a
|
||||
* 30px filled chip pill that opens a menu of options. The trigger reuses
|
||||
* `chipVariants` for visual parity with `Chip`; the chevron is owned by the
|
||||
* component and rendered in a 16px slot so the label's trailing gap matches a
|
||||
* leading icon's.
|
||||
*
|
||||
* @example
|
||||
* <ChipDropdown
|
||||
* value={currentLang}
|
||||
* onChange={setLanguage}
|
||||
* options={LANGUAGE_OPTIONS}
|
||||
* align='end'
|
||||
* matchTriggerWidth={false}
|
||||
* />
|
||||
*/
|
||||
const ChipDropdown = forwardRef<HTMLButtonElement, ChipDropdownProps>(function ChipDropdown(
|
||||
{
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
align = 'end',
|
||||
matchTriggerWidth = true,
|
||||
contentClassName,
|
||||
disabled,
|
||||
leftIcon: LeftIcon,
|
||||
className,
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const selected = options.find((option) => option.value === value)
|
||||
const displayLabel = selected?.label ?? placeholder ?? 'Select...'
|
||||
const isPlaceholder = selected == null
|
||||
|
||||
return (
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger asChild disabled={disabled}>
|
||||
<button
|
||||
ref={ref}
|
||||
type='button'
|
||||
disabled={disabled}
|
||||
className={cn(chipVariants({ variant: 'filled' }), TRIGGER_BORDER_CLASS, className)}
|
||||
>
|
||||
{LeftIcon ? (
|
||||
<LeftIcon className='size-[16px] flex-shrink-0 text-[var(--text-icon)]' />
|
||||
) : null}
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-0 flex-1 truncate text-sm',
|
||||
isPlaceholder ? 'text-[var(--text-muted)]' : 'text-[var(--text-body)]'
|
||||
)}
|
||||
>
|
||||
{displayLabel}
|
||||
</span>
|
||||
<ChipChevronDown />
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align={align}
|
||||
className={cn(
|
||||
matchTriggerWidth && 'w-[var(--radix-dropdown-menu-trigger-width)]',
|
||||
contentClassName
|
||||
)}
|
||||
>
|
||||
{options.map((option) => {
|
||||
const isSelected = option.value === value
|
||||
const OptionIcon = option.icon
|
||||
return (
|
||||
<DropdownMenuItem key={option.value} onSelect={() => onChange?.(option.value)}>
|
||||
{option.iconElement ?? (OptionIcon ? <OptionIcon /> : null)}
|
||||
<span>{option.label}</span>
|
||||
{isSelected ? <Check className='!ml-auto !size-[16px]' /> : null}
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
})
|
||||
|
||||
export { ChipDropdown }
|
||||
export type { ChipDropdownOption, ChipDropdownProps }
|
||||
@@ -1,175 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
type AnchorHTMLAttributes,
|
||||
type ButtonHTMLAttributes,
|
||||
type ComponentType,
|
||||
forwardRef,
|
||||
type ReactNode,
|
||||
} from 'react'
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
import Link, { type LinkProps } from 'next/link'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* Docs-local mirror of the platform chip chrome
|
||||
* (`apps/sim/components/emcn/components/chip/chip-chrome.ts`). The docs app
|
||||
* cannot import from `apps/sim`, so the canonical tokens are replicated here —
|
||||
* keep them in sync with the emcn source.
|
||||
*/
|
||||
export const chipContentGap = 'gap-1.5'
|
||||
/** Chip pill geometry — height, centering, gap, radius, padding, text size. */
|
||||
export const chipGeometryClass = `h-[30px] items-center ${chipContentGap} rounded-lg px-2 text-left text-sm`
|
||||
/** Chip-content icon (non-inverse): 16px, non-shrinking, `--text-icon`. */
|
||||
export const chipContentIconClass = 'size-[16px] flex-shrink-0 text-[var(--text-icon)]'
|
||||
/** Chip-content label (non-inverse): truncating `--text-body` at `text-sm`. */
|
||||
export const chipContentLabelClass = 'min-w-0 truncate text-[var(--text-body)] text-sm'
|
||||
/** The filled FILL (surface only, no border) — `--surface-5` light / `--surface-4` dark. */
|
||||
export const chipFilledFillTokens = 'bg-[var(--surface-5)] dark:bg-[var(--surface-4)]'
|
||||
/** The inverse/primary FILL — dark surface + inverse text in light, white + `--bg` text in dark. */
|
||||
const chipPrimaryFillTokens =
|
||||
'bg-[var(--text-primary)] text-[var(--text-inverse)] dark:bg-white dark:text-[var(--bg)]'
|
||||
/** 1px `--border-1` border applied to chip triggers so they read as controls. */
|
||||
export const TRIGGER_BORDER_CLASS = 'border border-[var(--border-1)]'
|
||||
|
||||
/**
|
||||
* 30px pill — the platform's most common chrome pattern, mirrored for the docs.
|
||||
*
|
||||
* @remarks
|
||||
* The implicit default variant is the bare pill — transparent, `--surface-active`
|
||||
* on hover. `filled` adds the filled surface; `primary` is the canonical inverse
|
||||
* CTA surface (dark in light mode, white in dark mode) used for the "Get started"
|
||||
* link.
|
||||
*/
|
||||
const chipVariants = cva(
|
||||
`group cursor-pointer font-season ${chipGeometryClass} transition-colors disabled:cursor-not-allowed disabled:opacity-60`,
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'hover:bg-[var(--surface-active)]',
|
||||
filled: `${chipFilledFillTokens} hover:bg-[var(--surface-active)]`,
|
||||
primary: `${chipPrimaryFillTokens} hover:bg-[var(--text-body)] dark:hover:bg-[var(--text-secondary)]`,
|
||||
},
|
||||
fullWidth: { true: 'flex', false: 'inline-flex' },
|
||||
},
|
||||
defaultVariants: { variant: 'default', fullWidth: false },
|
||||
}
|
||||
)
|
||||
|
||||
type ChipIcon = ComponentType<{ className?: string }>
|
||||
|
||||
type ChipVariant = VariantProps<typeof chipVariants>['variant']
|
||||
|
||||
interface ChipBaseProps extends VariantProps<typeof chipVariants> {
|
||||
/** Icon component rendered before the label. */
|
||||
leftIcon?: ChipIcon
|
||||
/** Icon component rendered after the label. */
|
||||
rightIcon?: ChipIcon
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* `primary` sets text color on the chip itself — its icon and label inherit via
|
||||
* `currentColor`. The default and `filled` chips use explicit icon
|
||||
* (`--text-icon`) and label (`--text-body`) colors.
|
||||
*/
|
||||
function ChipContent({
|
||||
variant,
|
||||
leftIcon: LeftIcon,
|
||||
rightIcon: RightIcon,
|
||||
children,
|
||||
}: ChipBaseProps) {
|
||||
const isInverse = variant === 'primary'
|
||||
const iconClass = cn(chipContentIconClass, isInverse && 'text-current')
|
||||
const labelClass = cn(chipContentLabelClass, 'flex-1', isInverse && 'text-current')
|
||||
return (
|
||||
<>
|
||||
{LeftIcon ? <LeftIcon className={iconClass} /> : null}
|
||||
{children != null && children !== false ? (
|
||||
<span className={labelClass}>{children}</span>
|
||||
) : null}
|
||||
{RightIcon ? <RightIcon className={iconClass} /> : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
interface ChipProps
|
||||
extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'>,
|
||||
ChipBaseProps {}
|
||||
|
||||
/**
|
||||
* @example <Chip leftIcon={Copy} onClick={copyPage}>Copy page</Chip>
|
||||
*/
|
||||
const Chip = forwardRef<HTMLButtonElement, ChipProps>(function Chip(
|
||||
{ className, variant, fullWidth, leftIcon, rightIcon, children, type, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type={type ?? 'button'}
|
||||
className={cn(chipVariants({ variant, fullWidth }), className)}
|
||||
{...props}
|
||||
>
|
||||
<ChipContent variant={variant} leftIcon={leftIcon} rightIcon={rightIcon}>
|
||||
{children}
|
||||
</ChipContent>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
interface ChipLinkProps
|
||||
extends Omit<LinkProps, 'children'>,
|
||||
Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps | 'children'>,
|
||||
ChipBaseProps {}
|
||||
|
||||
/**
|
||||
* @example <ChipLink href='https://sim.ai' variant='primary'>Get started</ChipLink>
|
||||
*/
|
||||
const ChipLink = forwardRef<HTMLAnchorElement, ChipLinkProps>(function ChipLink(
|
||||
{ className, variant, fullWidth, leftIcon, rightIcon, children, ...props },
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<Link ref={ref} className={cn(chipVariants({ variant, fullWidth }), className)} {...props}>
|
||||
<ChipContent variant={variant} leftIcon={leftIcon} rightIcon={rightIcon}>
|
||||
{children}
|
||||
</ChipContent>
|
||||
</Link>
|
||||
)
|
||||
})
|
||||
|
||||
interface ChipChevronDownProps {
|
||||
/** Layout-only extras (e.g. `ml-auto` to push the chevron flush right). Never chrome. */
|
||||
className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical trailing chevron adornment for chip-style dropdown triggers — a
|
||||
* 16px hidden-from-AT slot centering the 10×6 chevron glyph in `--text-icon`,
|
||||
* matching the chevron `ChipDropdown` owns in the main app.
|
||||
*/
|
||||
export function ChipChevronDown({ className }: ChipChevronDownProps) {
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
'inline-flex size-[16px] flex-shrink-0 items-center justify-center text-[var(--text-icon)]',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<svg width='10' height='6' viewBox='0 0 10 6' fill='none'>
|
||||
<path
|
||||
d='M1 1L5 5L9 1'
|
||||
stroke='currentColor'
|
||||
strokeWidth='1.33'
|
||||
strokeLinecap='square'
|
||||
strokeLinejoin='miter'
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export { Chip, ChipLink, chipVariants }
|
||||
export type { ChipLinkProps, ChipProps, ChipVariant }
|
||||
@@ -1,61 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { ComponentPropsWithRef } from 'react'
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const ANIMATION_CLASSES =
|
||||
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=open]:animate-in motion-reduce:animate-none'
|
||||
|
||||
/**
|
||||
* Menu surface mirrored from the emcn `DropdownMenu`
|
||||
* (`apps/sim/components/emcn/components/dropdown-menu/dropdown-menu.tsx`) —
|
||||
* `--bg` surface, `--border` outline, `rounded-xl`, `p-1.5`.
|
||||
*/
|
||||
const CONTENT_BASE_CLASSES =
|
||||
'z-50 max-h-[240px] min-w-[8rem] origin-[var(--radix-dropdown-menu-content-transform-origin)] overflow-y-auto overflow-x-hidden overscroll-none border border-[var(--border)] bg-[var(--bg)] p-1.5 font-season text-[var(--text-body)] shadow-sm'
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
|
||||
function DropdownMenuContent({
|
||||
className,
|
||||
sideOffset = 6,
|
||||
ref,
|
||||
...props
|
||||
}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.Content>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
ANIMATION_CLASSES,
|
||||
CONTENT_BASE_CLASSES,
|
||||
'max-w-[220px] rounded-xl',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownMenuItem({
|
||||
className,
|
||||
ref,
|
||||
...props
|
||||
}: ComponentPropsWithRef<typeof DropdownMenuPrimitive.Item>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex h-[30px] min-w-0 cursor-pointer select-none items-center gap-2 rounded-lg px-2 text-[13px] text-[var(--text-body)] outline-none transition-colors focus:bg-[var(--surface-active)] data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>span]:min-w-0 [&>span]:truncate [&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0 [&_svg]:text-[var(--text-icon)]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem }
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { ChipDropdown } from '@sim/emcn'
|
||||
import { useParams, usePathname, useRouter } from 'next/navigation'
|
||||
import { ChipDropdown } from '@/components/ui/chip-dropdown'
|
||||
|
||||
const languages = {
|
||||
en: { name: 'English', flag: '🇺🇸' },
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import { Search } from 'lucide-react'
|
||||
import {
|
||||
chipContentIconClass,
|
||||
chipFilledFillTokens,
|
||||
chipGeometryClass,
|
||||
TRIGGER_BORDER_CLASS,
|
||||
} from '@/components/ui/chip'
|
||||
} from '@sim/emcn'
|
||||
import { Search } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
export function SearchTrigger() {
|
||||
|
||||
@@ -5,6 +5,7 @@ const withMDX = createMDX()
|
||||
|
||||
const config: NextConfig = {
|
||||
reactStrictMode: true,
|
||||
transpilePackages: ['@sim/emcn'],
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"@ai-sdk/openai": "2.0.107",
|
||||
"@ai-sdk/react": "2.0.205",
|
||||
"@sim/db": "workspace:*",
|
||||
"@sim/emcn": "workspace:*",
|
||||
"@vercel/og": "^0.6.5",
|
||||
"ai": "5.0.203",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { cn } from '@sim/emcn'
|
||||
import AuthBackgroundSVG from '@/app/(auth)/components/auth-background-svg'
|
||||
|
||||
type AuthBackgroundProps = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { type ReactNode, useState } from 'react'
|
||||
import { Button } from '@/components/emcn'
|
||||
import { Button } from '@sim/emcn'
|
||||
import { GithubIcon, GoogleIcon, MicrosoftIcon } from '@/components/icons'
|
||||
import { client } from '@/lib/auth/auth-client'
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { Button, cn } from '@sim/emcn'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Button } from '@/components/emcn'
|
||||
import { getEnv, isTruthy } from '@/lib/core/config/env'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
|
||||
|
||||
interface SSOLoginButtonProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function LoginLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import {
|
||||
ChipModal,
|
||||
ChipModalBody,
|
||||
@@ -13,16 +8,21 @@ import {
|
||||
ChipModalField,
|
||||
ChipModalFooter,
|
||||
ChipModalHeader,
|
||||
cn,
|
||||
Input,
|
||||
Label,
|
||||
Loader,
|
||||
} from '@/components/emcn'
|
||||
} from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import { forgetPasswordContract } from '@/lib/api/contracts'
|
||||
import { client } from '@/lib/auth/auth-client'
|
||||
import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env'
|
||||
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
import { captureClientEvent } from '@/lib/posthog/client'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function ResetPasswordLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { cn, Input, Label, Loader } from '@sim/emcn'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import { Input, Label, Loader } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
|
||||
|
||||
interface RequestResetFormProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function SignupLoading() {
|
||||
return (
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
|
||||
import { Suspense, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
|
||||
import { cn, Input, Label, Loader } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { usePostHog } from 'posthog-js/react'
|
||||
import { Input, Label, Loader } from '@/components/emcn'
|
||||
import { client, useSession } from '@/lib/auth/auth-client'
|
||||
import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env'
|
||||
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
import { captureClientEvent, captureEvent } from '@/lib/posthog/client'
|
||||
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function SSOLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function VerifyLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { Suspense, useEffect, useState } from 'react'
|
||||
import { cn, InputOTP, InputOTPGroup, InputOTPSlot, Loader } from '@sim/emcn'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { InputOTP, InputOTPGroup, InputOTPSlot, Loader } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
|
||||
import { useVerification } from '@/app/(auth)/verify/use-verification'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function BlogPostLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@sim/emcn'
|
||||
import type { Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/emcn'
|
||||
import { FAQ } from '@/lib/blog/faq'
|
||||
import { getAllPostMeta, getPostBySlug, getRelatedPosts } from '@/lib/blog/registry'
|
||||
import { buildPostGraphJsonLd, buildPostMetadata } from '@/lib/blog/seo'
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@sim/emcn'
|
||||
import { Duplicate } from '@sim/emcn/icons'
|
||||
import { Share2 } from 'lucide-react'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/emcn'
|
||||
import { Duplicate } from '@/components/emcn/icons'
|
||||
import { LinkedInIcon, xIcon as XIcon } from '@/components/icons'
|
||||
|
||||
interface ShareButtonProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
const SKELETON_POST_COUNT = 4
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import NextImage from 'next/image'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { Lightbox } from '@/app/(landing)/blog/components/lightbox'
|
||||
|
||||
interface BlogImageProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function BlogLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Badge } from '@sim/emcn'
|
||||
import type { Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { getAllPostMeta } from '@/lib/blog/registry'
|
||||
import { buildCollectionPageJsonLd } from '@/lib/blog/seo'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
const SKELETON_TAG_COUNT = 12
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { X } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
Loader,
|
||||
Modal,
|
||||
@@ -13,7 +9,11 @@ import {
|
||||
ModalDescription,
|
||||
ModalTitle,
|
||||
ModalTrigger,
|
||||
} from '@/components/emcn'
|
||||
} from '@sim/emcn'
|
||||
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'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import { type AuthProviderStatusResponse, getAuthProvidersContract } from '@/lib/api/contracts/auth'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useId, useRef, useState } from 'react'
|
||||
import { Badge } from '@sim/emcn'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
const AuthModal = dynamic(
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
import { useRef, useState } from 'react'
|
||||
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
|
||||
import { Combobox, Input, Textarea } from '@sim/emcn'
|
||||
import { Check } from '@sim/emcn/icons'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import Link from 'next/link'
|
||||
import { Combobox, Input, Textarea } from '@/components/emcn'
|
||||
import { Check } from '@/components/emcn/icons'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import {
|
||||
CONTACT_TOPIC_OPTIONS,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import {
|
||||
ChipCombobox,
|
||||
ChipInput,
|
||||
@@ -14,8 +12,10 @@ import {
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
ModalTrigger,
|
||||
} from '@/components/emcn'
|
||||
import { Check } from '@/components/emcn/icons'
|
||||
} from '@sim/emcn'
|
||||
import { Check } from '@sim/emcn/icons'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import {
|
||||
DEMO_REQUEST_COMPANY_SIZE_OPTIONS,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { type SVGProps, useEffect, useRef, useState } from 'react'
|
||||
import { AnimatePresence, domAnimation, LazyMotion, m, useInView } from 'framer-motion'
|
||||
import { Streamdown } from 'streamdown'
|
||||
import 'streamdown/styles.css'
|
||||
import { ChevronDown } from '@/components/emcn'
|
||||
import { Database, File, Library, Table, Workflow } from '@/components/emcn/icons'
|
||||
import { ChevronDown, cn } from '@sim/emcn'
|
||||
import { Database, File, Library, Table, Workflow } from '@sim/emcn/icons'
|
||||
import {
|
||||
AnthropicIcon,
|
||||
GeminiIcon,
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
SlackIcon,
|
||||
xAIIcon,
|
||||
} from '@/components/icons'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
interface FeaturesPreviewProps {
|
||||
activeTab: number
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useState } from 'react'
|
||||
import { Badge } from '@sim/emcn'
|
||||
import {
|
||||
domAnimation,
|
||||
LazyMotion,
|
||||
@@ -11,7 +12,6 @@ import {
|
||||
} from 'framer-motion'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Image from 'next/image'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { FeaturesPreview } from '@/app/(landing)/components/features/components/features-preview'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { cn, handleKeyboardActivation } from '@sim/emcn'
|
||||
import { ArrowUp } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { handleKeyboardActivation } from '@/lib/core/utils/keyboard'
|
||||
import { captureClientEvent } from '@/lib/posthog/client'
|
||||
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cn } from '@sim/emcn'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { FooterCTA } from '@/app/(landing)/components/footer/footer-cta'
|
||||
|
||||
const LINK_CLASS =
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { cn } from '@sim/emcn'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
const AuthModal = dynamic(
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useId, useState } from 'react'
|
||||
import { ChevronDown, cn } from '@sim/emcn'
|
||||
import { domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import { ChevronDown } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
interface LandingFAQItem {
|
||||
question: string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { File } from '@/components/emcn/icons'
|
||||
import { File } from '@sim/emcn/icons'
|
||||
import { DocxIcon, PdfIcon } from '@/components/icons/document-icons'
|
||||
import type {
|
||||
PreviewColumn,
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Blimp, Checkbox, ChevronDown } from '@sim/emcn'
|
||||
import { TypeBoolean, TypeNumber, TypeText } from '@sim/emcn/icons'
|
||||
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import { ArrowUp, Table } from 'lucide-react'
|
||||
import { Blimp, Checkbox, ChevronDown } from '@/components/emcn'
|
||||
import { TypeBoolean, TypeNumber, TypeText } from '@/components/emcn/icons'
|
||||
import { captureClientEvent } from '@/lib/posthog/client'
|
||||
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
|
||||
import { EASE_OUT } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Database } from '@/components/emcn/icons'
|
||||
import { Database } from '@sim/emcn/icons'
|
||||
import {
|
||||
AirtableIcon,
|
||||
AsanaIcon,
|
||||
|
||||
+3
-4
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo, useState } from 'react'
|
||||
import { ArrowUpDown, Badge, Library, ListFilter, Search } from '@/components/emcn'
|
||||
import type { BadgeProps } from '@/components/emcn/components/badge/badge'
|
||||
import { Download, Workflow } from '@/components/emcn/icons'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import type { BadgeProps } from '@sim/emcn'
|
||||
import { ArrowUpDown, Badge, cn, Library, ListFilter, Search } from '@sim/emcn'
|
||||
import { Download, Workflow } from '@sim/emcn/icons'
|
||||
|
||||
interface LogRow {
|
||||
id: string
|
||||
|
||||
+1
-1
@@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import { memo, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Blimp, BubbleChatPreview, ChevronDown, MoreHorizontal, Play } from '@sim/emcn'
|
||||
import { AnimatePresence, domMax, LazyMotion, m } from 'framer-motion'
|
||||
import { ArrowUp } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Blimp, BubbleChatPreview, ChevronDown, MoreHorizontal, Play } from '@/components/emcn'
|
||||
import { AgentIcon, HubspotIcon, OpenAIIcon, SalesforceIcon } from '@/components/icons'
|
||||
import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
|
||||
import { captureClientEvent } from '@/lib/posthog/client'
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { ArrowUpDown, ListFilter, Plus, Search } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { ArrowUpDown, cn, ListFilter, Plus, Search } from '@sim/emcn'
|
||||
|
||||
export interface PreviewColumn {
|
||||
id: string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Calendar } from '@/components/emcn/icons'
|
||||
import { Calendar } from '@sim/emcn/icons'
|
||||
import type {
|
||||
PreviewColumn,
|
||||
PreviewRow,
|
||||
|
||||
+2
-4
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import { ChevronDown, Home, Library } from '@/components/emcn'
|
||||
import { ChevronDown, cn, Home, Library } from '@sim/emcn'
|
||||
import {
|
||||
Calendar,
|
||||
Database,
|
||||
@@ -10,8 +9,7 @@ import {
|
||||
Settings,
|
||||
Table,
|
||||
Workflow,
|
||||
} from '@/components/emcn/icons'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
} from '@sim/emcn/icons'
|
||||
import type { PreviewWorkflow } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'
|
||||
|
||||
export type SidebarView =
|
||||
|
||||
+3
-4
@@ -1,8 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import { Checkbox } from '@/components/emcn'
|
||||
import { Checkbox, cn } from '@sim/emcn'
|
||||
import {
|
||||
ChevronDown,
|
||||
Columns3,
|
||||
@@ -11,8 +10,8 @@ import {
|
||||
TypeBoolean,
|
||||
TypeNumber,
|
||||
TypeText,
|
||||
} from '@/components/emcn/icons'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
} from '@sim/emcn/icons'
|
||||
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import type {
|
||||
PreviewColumn,
|
||||
PreviewRow,
|
||||
|
||||
+1
-1
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { memo } from 'react'
|
||||
import { Blimp } from '@sim/emcn'
|
||||
import { domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import { Database } from 'lucide-react'
|
||||
import { Handle, type NodeProps, Position } from 'reactflow'
|
||||
import { Blimp } from '@/components/emcn'
|
||||
import {
|
||||
AgentIcon,
|
||||
AnthropicIcon,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cn } from '@sim/emcn'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
export interface NavBlogPost {
|
||||
slug: string
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { GithubOutlineIcon } from '@/components/icons'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { SessionContext } from '@/app/_shell/providers/session-provider'
|
||||
import {
|
||||
BlogDropdown,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { Badge } from '@sim/emcn'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
const AuthModal = dynamic(
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Badge, ChevronDown, cn } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Badge, ChevronDown } from '@/components/emcn'
|
||||
import { LandingWorkflowSeedStorage } from '@/lib/core/utils/browser-storage'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { TEMPLATE_WORKFLOWS } from '@/app/(landing)/components/templates/template-workflows'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
|
||||
+1
-1
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { cn } from '@sim/emcn'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
|
||||
|
||||
interface TemplateCardButtonProps {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Loader } from '@/components/emcn'
|
||||
import { Loader } from '@sim/emcn'
|
||||
|
||||
export default function IntegrationDetailLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Badge } from '@sim/emcn'
|
||||
import type { Metadata } from 'next'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
import {
|
||||
blockTypeToIconMap,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Loader } from '@/components/emcn'
|
||||
import { Loader } from '@sim/emcn'
|
||||
|
||||
export default function IntegrationDetailLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { ChipInput, Search } from '@/components/emcn'
|
||||
import { ChipInput, Search } from '@sim/emcn'
|
||||
import { blockTypeToIconMap, formatIntegrationType, type Integration } from '@/lib/integrations'
|
||||
import { IntegrationRow } from '@/app/(landing)/integrations/components/integration-card'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ComponentType, ElementType, HTMLAttributes, SVGProps } from 'react'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { cn } from '@sim/emcn'
|
||||
|
||||
interface IntegrationIconProps extends HTMLAttributes<HTMLElement> {
|
||||
bgColor: string
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ChipModalField,
|
||||
ChipModalFooter,
|
||||
ChipModalHeader,
|
||||
} from '@/components/emcn'
|
||||
} from '@sim/emcn'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import { integrationRequestContract } from '@/lib/api/contracts/common'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Loader } from '@/components/emcn'
|
||||
import { Loader } from '@sim/emcn'
|
||||
|
||||
export default function ModelDetailLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Loader } from '@/components/emcn'
|
||||
import { Loader } from '@sim/emcn'
|
||||
|
||||
export default function ModelProviderLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Badge } from '@sim/emcn'
|
||||
import type { Metadata } from 'next'
|
||||
import Link from 'next/link'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Badge } from '@sim/emcn'
|
||||
import type { Metadata } from 'next'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
|
||||
import { ModelComparisonCharts } from '@/app/(landing)/models/components/model-comparison-charts'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Loader } from '@/components/emcn'
|
||||
import { Loader } from '@sim/emcn'
|
||||
|
||||
export default function ModelDetailLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Loader } from '@/components/emcn'
|
||||
import { Loader } from '@sim/emcn'
|
||||
|
||||
export default function ModelProviderLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo, useState } from 'react'
|
||||
import { Input } from '@sim/emcn'
|
||||
import Link from 'next/link'
|
||||
import { Input } from '@/components/emcn'
|
||||
import { ChevronArrow, ProviderIcon } from '@/app/(landing)/models/components/model-primitives'
|
||||
import {
|
||||
type CatalogModel,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Badge } from '@sim/emcn'
|
||||
import Link from 'next/link'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import {
|
||||
type CatalogModel,
|
||||
type CatalogProvider,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function PrivacyLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function TermsLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { Tooltip } from '@/components/emcn'
|
||||
import { Tooltip } from '@sim/emcn'
|
||||
|
||||
interface TooltipProviderProps {
|
||||
children: React.ReactNode
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo, useSyncExternalStore } from 'react'
|
||||
import { Loader } from '@sim/emcn'
|
||||
import { CheckCircle2, Circle, ExternalLink, GraduationCap } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { Loader } from '@/components/emcn'
|
||||
import {
|
||||
getCompletedLessonsFromSnapshot,
|
||||
getCompletedLessonsSnapshot,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import { CheckCircle2, XCircle } from 'lucide-react'
|
||||
import { markLessonComplete } from '@/lib/academy/local-progress'
|
||||
import type { QuizDefinition, QuizQuestion } from '@/lib/academy/types'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
interface LessonQuizProps {
|
||||
lessonId: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { sleep } from '@sim/utils/helpers'
|
||||
import type { Edge } from 'reactflow'
|
||||
@@ -12,7 +13,6 @@ import type {
|
||||
ValidationResult,
|
||||
} from '@/lib/academy/types'
|
||||
import { validateExercise } from '@/lib/academy/validation'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { getEffectiveBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
|
||||
import { getQueryClient } from '@/app/_shell/providers/get-query-client'
|
||||
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { cn } from '@sim/emcn'
|
||||
import { CheckCircle2, Circle } from 'lucide-react'
|
||||
import type { ValidationRuleResult } from '@/lib/academy/types'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
interface ValidationChecklistProps {
|
||||
results: ValidationRuleResult[]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React from 'react'
|
||||
import { Streamdown } from 'streamdown'
|
||||
import 'streamdown/styles.css'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/emcn'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@sim/emcn'
|
||||
import type { ChangelogEntry } from '@/app/changelog/components/changelog-content'
|
||||
|
||||
type Props = { initialEntries: ChangelogEntry[] }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function ChangelogLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function ChatLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { cn, Input, InputOTP, InputOTPGroup, InputOTPSlot, Label, Loader } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { Input, InputOTP, InputOTPGroup, InputOTPSlot, Label, Loader } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
import AuthBackground from '@/app/(auth)/components/auth-background'
|
||||
import { AUTH_SUBMIT_BTN, AUTH_TEXT_LINK } from '@/app/(auth)/components/auth-button-classes'
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { cn, Input, Label, Loader } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import { Input, Label, Loader } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import AuthBackground from '@/app/(auth)/components/auth-background'
|
||||
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
|
||||
import { SupportFooter } from '@/app/(auth)/components/support-footer'
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
|
||||
import type React from 'react'
|
||||
import { useCallback, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { Badge, cn, handleKeyboardActivation, Tooltip } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import { ArrowUp, Mic, Paperclip, X } from 'lucide-react'
|
||||
import { Badge, Tooltip } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { handleKeyboardActivation } from '@/lib/core/utils/keyboard'
|
||||
import { CHAT_ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
|
||||
import { VoiceInput } from '@/app/chat/components/input/voice-input'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export function ChatLoadingState() {
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('@/components/emcn', () => ({
|
||||
vi.mock('@sim/emcn', () => ({
|
||||
Button: () => null,
|
||||
Download: () => null,
|
||||
Loader: () => null,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Button, Download, Loader } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { sleep } from '@sim/utils/helpers'
|
||||
import { Music } from 'lucide-react'
|
||||
import { Button, Download, Loader } from '@/components/emcn'
|
||||
import { DefaultFileIcon, getDocumentIcon } from '@/components/icons/document-icons'
|
||||
import { getBrowserOrigin } from '@/lib/core/utils/urls'
|
||||
import type { ChatFile } from '@/app/chat/components/message/message'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { type HTMLAttributes, memo, type ReactNode } from 'react'
|
||||
import { Streamdown } from 'streamdown'
|
||||
import 'streamdown/styles.css'
|
||||
import { CopyCodeButton, Tooltip } from '@/components/emcn'
|
||||
import { CopyCodeButton, Tooltip } from '@sim/emcn'
|
||||
import { extractTextContent } from '@/lib/core/utils/react-node-text'
|
||||
|
||||
function LinkWithPreview({ href, children }: { href: string; children: React.ReactNode }) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('@/components/emcn', () => ({
|
||||
vi.mock('@sim/emcn', () => ({
|
||||
Duplicate: () => null,
|
||||
Tooltip: {},
|
||||
}))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { memo, useState } from 'react'
|
||||
import { Duplicate, Tooltip } from '@sim/emcn'
|
||||
import { Check, File as FileIcon, FileText, Image as ImageIcon } from 'lucide-react'
|
||||
import { Duplicate, Tooltip } from '@/components/emcn'
|
||||
import {
|
||||
ChatFileDownload,
|
||||
ChatFileDownloadAll,
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { type RefObject, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { cn } from '@sim/emcn'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { Mic, MicOff, Phone } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { requestJson } from '@/lib/api/client/request'
|
||||
import { speechTokenContract } from '@/lib/api/contracts/media/speech'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { arrayBufferToBase64, floatTo16BitPCM } from '@/lib/speech/audio'
|
||||
import {
|
||||
CHUNK_SEND_INTERVAL_MS,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { Skeleton } from '@sim/emcn'
|
||||
|
||||
export default function CredentialAccountLoading() {
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { cn, Input, Label, Loader } from '@sim/emcn'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { Eye, EyeOff } from 'lucide-react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Input, Label, Loader } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
|
||||
import { PublicFileAuthShell } from '@/app/f/[token]/public-file-auth-shell'
|
||||
import { usePublicFileAuth } from '@/hooks/queries/public-shares'
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { cn, Input, InputOTP, InputOTPGroup, InputOTPSlot, Label, Loader } from '@sim/emcn'
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Input, InputOTP, InputOTPGroup, InputOTPSlot, Label, Loader } from '@/components/emcn'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
import { AUTH_SUBMIT_BTN, AUTH_TEXT_LINK } from '@/app/(auth)/components/auth-button-classes'
|
||||
import { PublicFileAuthShell } from '@/app/f/[token]/public-file-auth-shell'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user