From 424a1e35e5648c0bda448a283c06488e2ed4a489 Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Mon, 15 Jun 2026 01:41:05 -0700 Subject: [PATCH 1/4] improvement(scheduled-tasks): move recurrence into modal body as a section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the footer RecurrenceControl (a row of chip dropdowns) with a RecurrenceSection rendered between the prompt body and footer: a "Recurring" Switch toggles one-time vs repeat, and — once on — frequency and end (never, on a date, after N runs) are labeled ChipModalField rows aligned to the modal header/footer gutter. Toggling Recurring off now preserves the recurrence shape (cadence, end, and a passed-through custom cron) and only sets frequency: 'once', so toggling back on restores a conversationally-authored custom schedule instead of silently rewriting it to daily. Also restore the prompt editor's native scale (text-[15px], -0.015em tracking) so the editor reads the same in the chat input and the task modal body. --- .../user-input/components/constants.ts | 16 +- .../task-modal/recurrence-control.tsx | 131 -------------- .../task-modal/recurrence-section.tsx | 166 ++++++++++++++++++ .../components/task-modal/task-modal.tsx | 29 ++- 4 files changed, 184 insertions(+), 158 deletions(-) delete mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-control.tsx create mode 100644 apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts index 9fb5b05d04a..a1065032200 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/constants.ts @@ -47,27 +47,25 @@ export interface PlusMenuHandle { /** * Box and typography shared by the textarea and its mirror overlay — both must * produce identical line wrapping so the overlay text sits exactly over the - * (transparent) textarea text. The scale is the canonical chip text-field - * scale ({@link ChipTextarea}: `text-sm`, default tracking), so the editor - * reads identically in the chat input and inside chip modals — one size, - * everywhere. + * (transparent) textarea text. The scale is the chat input's native prompt + * scale (`text-[15px]`, `-0.015em` tracking); the task modal's body inherits it + * so the editor reads the same whether it's the chat input or inside the modal. */ const FIELD_MIRROR_CLASSES = cn( - 'm-0 box-border min-h-[20px] w-full break-words [overflow-wrap:anywhere] border-0 bg-transparent', - 'px-1 py-1 font-body text-sm leading-[20px]' + 'm-0 box-border min-h-[24px] w-full break-words [overflow-wrap:anywhere] border-0 bg-transparent', + 'px-1 py-1 font-body text-[15px] leading-[24px] tracking-[-0.015em]' ) /** * The textarea grows to its full content height (`h-auto`, no internal scroll); * the shared scroller clips and scrolls it. Its text is transparent so the - * mirror overlay shows through; only the caret paints. The placeholder uses - * the canonical `--text-muted`, matching every other chip text field. + * mirror overlay shows through; only the caret paints. */ export const TEXTAREA_BASE_CLASSES = cn( FIELD_MIRROR_CLASSES, 'block h-auto resize-none overflow-hidden', 'text-transparent caret-[var(--text-primary)] outline-none', - 'placeholder:text-[var(--text-muted)]', + 'placeholder:font-[380] placeholder:text-[var(--text-subtle)]', 'focus-visible:ring-0 focus-visible:ring-offset-0' ) diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-control.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-control.tsx deleted file mode 100644 index 54cc65d5720..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-control.tsx +++ /dev/null @@ -1,131 +0,0 @@ -'use client' - -import { format } from 'date-fns' -import { ChipDatePicker, ChipDropdown, ChipInput, RefreshCw } from '@/components/emcn' -import type { Recurrence } from '@/app/workspace/[workspaceId]/scheduled-tasks/utils/recurrence' - -const WEEKDAY_PRESET = [1, 2, 3, 4, 5] -/** Seed count when the user first chooses "ends after N runs". */ -const DEFAULT_END_AFTER_COUNT = 10 - -/** The frequency presets the dropdown authors, keyed by a synthetic option value. */ -type FrequencyOption = 'once' | 'daily' | 'weekly' | 'weekdays' | 'monthly' | 'custom' - -function isWeekdayPreset(weekdays: number[]): boolean { - return ( - weekdays.length === WEEKDAY_PRESET.length && WEEKDAY_PRESET.every((d) => weekdays.includes(d)) - ) -} - -/** Collapses a recurrence into the single dropdown value that represents it. */ -function frequencyOptionFor(recurrence: Recurrence): FrequencyOption { - if (recurrence.frequency === 'weekly') - return isWeekdayPreset(recurrence.weekdays) ? 'weekdays' : 'weekly' - return recurrence.frequency -} - -interface RecurrenceControlProps { - recurrence: Recurrence - onChange: (recurrence: Recurrence) => void - /** The launch day, so weekly/monthly labels name the weekday and day-of-month. */ - launchDate: string -} - -/** - * The repeat + end controls for a scheduled task, modeled on a calendar app's - * recurrence row: a frequency preset and — when the task repeats — how it ends - * (never, on a date, or after N runs). - */ -export function RecurrenceControl({ recurrence, onChange, launchDate }: RecurrenceControlProps) { - const launch = new Date(`${launchDate}T00:00`) - - const frequencyOptions = [ - { value: 'once', label: 'Once' }, - { value: 'daily', label: 'Daily' }, - { value: 'weekly', label: `Weekly on ${format(launch, 'EEE')}` }, - { value: 'weekdays', label: 'Weekdays' }, - { value: 'monthly', label: `Monthly on the ${format(launch, 'do')}` }, - ...(recurrence.frequency === 'custom' ? [{ value: 'custom', label: 'Custom' }] : []), - ] - - const handleFrequencyChange = (value: string) => { - const option = value as FrequencyOption - switch (option) { - case 'once': - onChange({ frequency: 'once', weekdays: [], end: { type: 'never' } }) - return - case 'daily': - onChange({ ...recurrence, frequency: 'daily', weekdays: [] }) - return - case 'weekly': - onChange({ ...recurrence, frequency: 'weekly', weekdays: [launch.getDay()] }) - return - case 'weekdays': - onChange({ ...recurrence, frequency: 'weekly', weekdays: [...WEEKDAY_PRESET] }) - return - case 'monthly': - onChange({ ...recurrence, frequency: 'monthly', weekdays: [] }) - return - case 'custom': - onChange({ ...recurrence, frequency: 'custom' }) - } - } - - const handleEndChange = (value: string) => { - if (value === 'never') onChange({ ...recurrence, end: { type: 'never' } }) - else if (value === 'on') - onChange({ ...recurrence, end: { type: 'on', date: format(launch, 'yyyy-MM-dd') } }) - else { - const count = recurrence.end.type === 'after' ? recurrence.end.count : DEFAULT_END_AFTER_COUNT - onChange({ ...recurrence, end: { type: 'after', count } }) - } - } - - return ( -
- - - {recurrence.frequency !== 'once' && ( - - )} - - {recurrence.frequency !== 'once' && recurrence.end.type === 'on' && ( - onChange({ ...recurrence, end: { type: 'on', date } })} - flush - /> - )} - - {recurrence.frequency !== 'once' && recurrence.end.type === 'after' && ( - { - const count = Math.max(1, Math.floor(Number(event.target.value) || 1)) - onChange({ ...recurrence, end: { type: 'after', count } }) - }} - endAdornment={runs} - /> - )} -
- ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx new file mode 100644 index 00000000000..fa3ac2cd551 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx @@ -0,0 +1,166 @@ +'use client' + +import { format } from 'date-fns' +import { ChipDatePicker, ChipModalField, Switch } from '@/components/emcn' +import type { Recurrence } from '@/app/workspace/[workspaceId]/scheduled-tasks/utils/recurrence' + +const WEEKDAY_PRESET = [1, 2, 3, 4, 5] +/** Seed count when the user first chooses "ends after N runs". */ +const DEFAULT_END_AFTER_COUNT = 10 +/** Cadence a task falls back to when the user first flips on recurrence. */ +const DEFAULT_RECURRING_FREQUENCY = 'daily' + +/** The frequency presets the dropdown authors, keyed by a synthetic option value. */ +type FrequencyOption = 'daily' | 'weekly' | 'weekdays' | 'monthly' | 'custom' + +function isWeekdayPreset(weekdays: number[]): boolean { + return ( + weekdays.length === WEEKDAY_PRESET.length && WEEKDAY_PRESET.every((d) => weekdays.includes(d)) + ) +} + +/** Collapses a recurring recurrence into the single dropdown value that represents it. */ +function frequencyOptionFor(recurrence: Recurrence): FrequencyOption { + if (recurrence.frequency === 'weekly') + return isWeekdayPreset(recurrence.weekdays) ? 'weekdays' : 'weekly' + if (recurrence.frequency === 'once') return DEFAULT_RECURRING_FREQUENCY + return recurrence.frequency +} + +interface RecurrenceSectionProps { + recurrence: Recurrence + onChange: (recurrence: Recurrence) => void + /** The launch day, so weekly/monthly labels name the weekday and day-of-month. */ + launchDate: string +} + +/** + * The repeat + end controls for a scheduled task, rendered as a body section + * below the prompt: a "Recurring" {@link Switch} that toggles a one-time launch + * into a repeat, and — once on — the frequency preset and how it ends (never, on + * a date, or after N runs). + * + * Composed as a sibling between the prompt body and footer; it owns its own + * leading separator and mirrors {@link ChipModalBody}'s spacing + * (`gap-4 px-2 pt-4 pb-4.5`) so every {@link ChipModalField} lands at the same + * effective `px-4` as the modal header/footer — no changes to the `ChipModal` + * primitives. + */ +export function RecurrenceSection({ recurrence, onChange, launchDate }: RecurrenceSectionProps) { + const launch = new Date(`${launchDate}T00:00`) + const isRecurring = recurrence.frequency !== 'once' + + const frequencyOptions = [ + { value: 'daily', label: 'Daily' }, + { value: 'weekly', label: `Weekly on ${format(launch, 'EEE')}` }, + { value: 'weekdays', label: 'Weekdays' }, + { value: 'monthly', label: `Monthly on the ${format(launch, 'do')}` }, + ...(recurrence.frequency === 'custom' ? [{ value: 'custom', label: 'Custom' }] : []), + ] + + /** + * Flips the one-time launch into a repeat and back. Toggling off keeps the + * recurrence shape (cadence, end, and a passed-through `custom` cron) on the + * object and only sets `frequency: 'once'` — the wire ignores everything but + * `frequency` for a one-time task — so toggling back on restores `custom` + * rather than silently rewriting a conversationally-authored cron to `daily`. + */ + const handleRecurringToggle = (checked: boolean) => { + if (!checked) { + onChange({ ...recurrence, frequency: 'once' }) + return + } + onChange({ + ...recurrence, + frequency: recurrence.cron ? 'custom' : DEFAULT_RECURRING_FREQUENCY, + weekdays: [], + }) + } + + const handleFrequencyChange = (value: string) => { + const option = value as FrequencyOption + switch (option) { + case 'daily': + onChange({ ...recurrence, frequency: 'daily', weekdays: [] }) + return + case 'weekly': + onChange({ ...recurrence, frequency: 'weekly', weekdays: [launch.getDay()] }) + return + case 'weekdays': + onChange({ ...recurrence, frequency: 'weekly', weekdays: [...WEEKDAY_PRESET] }) + return + case 'monthly': + onChange({ ...recurrence, frequency: 'monthly', weekdays: [] }) + return + case 'custom': + onChange({ ...recurrence, frequency: 'custom' }) + } + } + + const handleEndChange = (value: string) => { + if (value === 'never') onChange({ ...recurrence, end: { type: 'never' } }) + else if (value === 'on') + onChange({ ...recurrence, end: { type: 'on', date: format(launch, 'yyyy-MM-dd') } }) + else { + const count = recurrence.end.type === 'after' ? recurrence.end.count : DEFAULT_END_AFTER_COUNT + onChange({ ...recurrence, end: { type: 'after', count } }) + } + } + + return ( +
+
+
+ + + + + {isRecurring && ( + <> + + + + + {recurrence.end.type === 'on' && ( + + onChange({ ...recurrence, end: { type: 'on', date } })} + fullWidth + /> + + )} + + {recurrence.end.type === 'after' && ( + { + const count = Math.max(1, Math.floor(Number(value) || 1)) + onChange({ ...recurrence, end: { type: 'after', count } }) + }} + /> + )} + + )} +
+
+ ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsx index 829423504f3..0d05b31fdd9 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/task-modal.tsx @@ -18,7 +18,7 @@ import { PromptEditor, usePromptEditor, } from '@/app/workspace/[workspaceId]/home/components/user-input/components' -import { RecurrenceControl } from '@/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-control' +import { RecurrenceSection } from '@/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section' import type { CalendarSlot } from '@/app/workspace/[workspaceId]/scheduled-tasks/hooks/use-calendar' import { DEFAULT_RECURRENCE, @@ -117,10 +117,10 @@ interface TaskModalProps { /** * The "schedule a task" modal, shared by create (blank, or pre-filled from a - * duplicate) and edit (seeded from a task's schedule). The body is one prompt - * surface — the chat input's editor, so `@` mentions resources and `/` invokes - * skills exactly like talking to Sim — and the footer carries the recurrence, - * launch date/time, and (edit only) Delete. + * duplicate) and edit (seeded from a task's schedule). The body is the chat + * input's editor — so `@` mentions resources and `/` invokes skills exactly like + * talking to Sim — followed by the recurrence section; the footer carries the + * launch date/time and (edit only) Delete. */ export function TaskModal({ open, @@ -296,10 +296,11 @@ function TaskModalContent({ } /** - * Footer secondary actions. Delete is disabled while `submitting` because it - * bypasses the dismiss guard — it closes the modal via `closeTask`, not the - * guarded `onOpenChange` — so without the lock an in-flight edit and a delete - * could run against the same task at once. + * Footer secondary actions — the launch date/time pickers and (edit only) + * Delete. Delete is disabled while `submitting` because it bypasses the + * dismiss guard — it closes the modal via `closeTask`, not the guarded + * `onOpenChange` — so without the lock an in-flight edit and a delete could + * run against the same task at once. Recurrence lives in the body, not here. */ const secondaryActions: ChipModalFooterSlotAction[] = [ ...(edit && onRequestDelete @@ -312,15 +313,6 @@ function TaskModalContent({ }, ] : []), - { - custom: ( - - ), - }, { custom: }, { custom: }, ] @@ -338,6 +330,7 @@ function TaskModalContent({ onSubmit={handleSubmit} /> + Date: Mon, 15 Jun 2026 01:49:44 -0700 Subject: [PATCH 2/4] fix(scheduled-tasks): clear custom cron when switching frequency away from custom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Recurring toggle restores `frequency: 'custom'` when `recurrence.cron` is truthy, but switching the frequency dropdown away from custom kept the stale cron on the object — so editing a custom-cron task to Daily, then toggling Recurring off and back on, snapped it back to Custom and persisted the old cron. Clear `cron` in the non-custom frequency branches so it is present only while the cadence is genuinely custom (matching the type's "custom only" invariant), making the toggle's restore signal accurate. Also document the unreachable `once` branch in frequencyOptionFor as a type-exhaustiveness fallback (keeps the return type without a cast). --- .../task-modal/recurrence-section.tsx | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx index fa3ac2cd551..17226591e49 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx @@ -23,6 +23,9 @@ function isWeekdayPreset(weekdays: number[]): boolean { function frequencyOptionFor(recurrence: Recurrence): FrequencyOption { if (recurrence.frequency === 'weekly') return isWeekdayPreset(recurrence.weekdays) ? 'weekdays' : 'weekly' + // Exhaustiveness fallback: callers gate on `isRecurring`, so `once` never + // reaches here at runtime, but the dropdown can't represent it — mapping it to + // a recurring default keeps the return type `FrequencyOption` without a cast. if (recurrence.frequency === 'once') return DEFAULT_RECURRING_FREQUENCY return recurrence.frequency } @@ -81,16 +84,26 @@ export function RecurrenceSection({ recurrence, onChange, launchDate }: Recurren const option = value as FrequencyOption switch (option) { case 'daily': - onChange({ ...recurrence, frequency: 'daily', weekdays: [] }) + onChange({ ...recurrence, frequency: 'daily', weekdays: [], cron: undefined }) return case 'weekly': - onChange({ ...recurrence, frequency: 'weekly', weekdays: [launch.getDay()] }) + onChange({ + ...recurrence, + frequency: 'weekly', + weekdays: [launch.getDay()], + cron: undefined, + }) return case 'weekdays': - onChange({ ...recurrence, frequency: 'weekly', weekdays: [...WEEKDAY_PRESET] }) + onChange({ + ...recurrence, + frequency: 'weekly', + weekdays: [...WEEKDAY_PRESET], + cron: undefined, + }) return case 'monthly': - onChange({ ...recurrence, frequency: 'monthly', weekdays: [] }) + onChange({ ...recurrence, frequency: 'monthly', weekdays: [], cron: undefined }) return case 'custom': onChange({ ...recurrence, frequency: 'custom' }) From 857c39d380026c182d2551bbac896a40dae99384 Mon Sep 17 00:00:00 2001 From: Emir Karabeg Date: Mon, 15 Jun 2026 01:55:10 -0700 Subject: [PATCH 3/4] fix(scheduled-tasks): restore the prior cadence when re-enabling recurrence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Toggling Recurring off collapsed frequency to 'once' but toggling back on forced 'daily' and cleared weekdays, so pausing a weekly/weekdays/monthly task and re-enabling it silently reset it to daily. Cache the last recurring cadence in a ref (written during render) and reinstate it on toggle-on, so a paused "Weekly on Mon" returns as weekly. This also subsumes the custom-cron restore — the ref remembers 'custom' across the one-time interval — so the toggle no longer special-cases cron. --- .../task-modal/recurrence-section.tsx | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx index 17226591e49..ec6964a751d 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx @@ -1,8 +1,12 @@ 'use client' +import { useRef } from 'react' import { format } from 'date-fns' import { ChipDatePicker, ChipModalField, Switch } from '@/components/emcn' -import type { Recurrence } from '@/app/workspace/[workspaceId]/scheduled-tasks/utils/recurrence' +import type { + Recurrence, + RecurrenceFrequency, +} from '@/app/workspace/[workspaceId]/scheduled-tasks/utils/recurrence' const WEEKDAY_PRESET = [1, 2, 3, 4, 5] /** Seed count when the user first chooses "ends after N runs". */ @@ -50,6 +54,16 @@ interface RecurrenceSectionProps { * primitives. */ export function RecurrenceSection({ recurrence, onChange, launchDate }: RecurrenceSectionProps) { + /** + * The cadence to reinstate when recurrence is toggled back on. Toggling off + * collapses `frequency` to `once`, dropping which preset was active, so the + * last recurring cadence is cached here and restored — a paused "Weekly on + * Mon" returns as weekly, not silently reset to daily. Written during render + * (an idempotent cache), so it is current before the toggle handler reads it. + */ + const lastRecurringFrequency = useRef(DEFAULT_RECURRING_FREQUENCY) + if (recurrence.frequency !== 'once') lastRecurringFrequency.current = recurrence.frequency + const launch = new Date(`${launchDate}T00:00`) const isRecurring = recurrence.frequency !== 'once' @@ -63,21 +77,13 @@ export function RecurrenceSection({ recurrence, onChange, launchDate }: Recurren /** * Flips the one-time launch into a repeat and back. Toggling off keeps the - * recurrence shape (cadence, end, and a passed-through `custom` cron) on the - * object and only sets `frequency: 'once'` — the wire ignores everything but - * `frequency` for a one-time task — so toggling back on restores `custom` - * rather than silently rewriting a conversationally-authored cron to `daily`. + * recurrence shape (weekdays, end, and a passed-through `custom` cron) on the + * object and only collapses `frequency` to `once`; toggling back on reinstates + * the remembered cadence, so neither a weekly preset nor a conversationally + * authored custom cron is silently rewritten to daily. */ const handleRecurringToggle = (checked: boolean) => { - if (!checked) { - onChange({ ...recurrence, frequency: 'once' }) - return - } - onChange({ - ...recurrence, - frequency: recurrence.cron ? 'custom' : DEFAULT_RECURRING_FREQUENCY, - weekdays: [], - }) + onChange({ ...recurrence, frequency: checked ? lastRecurringFrequency.current : 'once' }) } const handleFrequencyChange = (value: string) => { From cf57926b33edcf915f2ddf7b0cd272c5276094a8 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 15 Jun 2026 10:14:39 -0700 Subject: [PATCH 4/4] improvement(scheduled-tasks): compose canonical modal separator, tidy imports Replace the recurrence section's hand-rolled `h-px bg-[var(--border)]` divider with the canonical ChipModalSeparator (now exported from the chip-modal barrel) so the modal's hairline has a single source of truth. Also unify loading.tsx icon imports onto the @/components/emcn barrel. --- .../components/task-modal/recurrence-section.tsx | 4 ++-- .../workspace/[workspaceId]/scheduled-tasks/loading.tsx | 3 +-- .../components/emcn/components/chip-modal/chip-modal.tsx | 8 ++++++-- apps/sim/components/emcn/components/index.ts | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx index ec6964a751d..d74ca387fb0 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/components/task-modal/recurrence-section.tsx @@ -2,7 +2,7 @@ import { useRef } from 'react' import { format } from 'date-fns' -import { ChipDatePicker, ChipModalField, Switch } from '@/components/emcn' +import { ChipDatePicker, ChipModalField, ChipModalSeparator, Switch } from '@/components/emcn' import type { Recurrence, RecurrenceFrequency, @@ -128,7 +128,7 @@ export function RecurrenceSection({ recurrence, onChange, launchDate }: Recurren return (
-
+
diff --git a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx index 1b3a7fd7dea..d6850e44e2e 100644 --- a/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx +++ b/apps/sim/app/workspace/[workspaceId]/scheduled-tasks/loading.tsx @@ -1,7 +1,6 @@ 'use client' -import { Plus } from '@/components/emcn' -import { Calendar } from '@/components/emcn/icons' +import { Calendar, Plus } from '@/components/emcn' import { type ChromeActionSpec, ResourceChromeFallback, diff --git a/apps/sim/components/emcn/components/chip-modal/chip-modal.tsx b/apps/sim/components/emcn/components/chip-modal/chip-modal.tsx index fc6826aaee7..6d98dd2d875 100644 --- a/apps/sim/components/emcn/components/chip-modal/chip-modal.tsx +++ b/apps/sim/components/emcn/components/chip-modal/chip-modal.tsx @@ -62,8 +62,12 @@ import { Loader } from '@/components/emcn/icons' import { cn } from '@/lib/core/utils/cn' import { quickValidateEmail } from '@/lib/messaging/email/validation' -/** Shared inset separator used by the header and footer edges. */ -function ChipModalSeparator({ className }: { className?: string }) { +/** + * The modal's hairline divider — used by the header and footer edges, and + * exported so body sections (e.g. a settings band below a prompt) can draw the + * same line instead of re-deriving the `h-px bg-[var(--border)]` string. + */ +export function ChipModalSeparator({ className }: { className?: string }) { return
} diff --git a/apps/sim/components/emcn/components/index.ts b/apps/sim/components/emcn/components/index.ts index 96f730c5647..ae022fe63b2 100644 --- a/apps/sim/components/emcn/components/index.ts +++ b/apps/sim/components/emcn/components/index.ts @@ -53,6 +53,7 @@ export { ChipModalPromptBody, type ChipModalPromptBodyProps, type ChipModalProps, + ChipModalSeparator, type ChipModalTab, ChipModalTabs, type ChipModalTabsProps,