Skip to content

Commit d60747d

Browse files
authored
feat(mothership): draft persistence, new task eager creation, doc preview fix, and loading polish (#4361)
* feat(mothership): draft persistence, new task eager creation, doc preview fix, and loading polish - Add `useMothershipDraftsStore` (persist) to save and restore draft text, file attachments, and contexts per chat scope key; cleared on workspace reset - Add `useCreateTask` mutation that eagerly POSTs a new chat, optimistically prepends it to the sidebar task list, and navigates to `/task/:id` — removes the `id:'new'` placeholder pattern entirely - Fix docx/pptx/pdf preview: remove `!isDocFormat` guards in `file-preview-adapter` so preview sessions are created and streaming content is set during agent writes, suppressing intermediate compilation errors - Replace `Loader2` (lucide) with `Loader` (emcn) across auth, chat, knowledge, logs, deploy, and tool-input components; add `Clipboard` icon to emcn - Add `EditorContextMenu` to the Monaco file viewer - Expand Monaco `SIM_DARK` and `SIM_LIGHT` token rules with string.link, delimiter, tag, attribute, and Markdown tokens (strong, emphasis, variable) * fix(files): dispose onContextMenu listener and handle clipboard rejection * fix(tasks): clear workspaceId:new draft after eager task creation * fix(user-input): always update prevDefaultValueRef on defaultValue change Prevents stale ref when defaultValue transitions through empty — if defaultValue goes non-empty → empty → same non-empty value, the ref was not updated on the empty transition, causing setValue to be skipped on the subsequent change. Also removes unnecessary useCallback wrapping from handleSendQueuedHead, handleEditQueued, handleEditQueuedTail in MothershipChat — none have a reference observer (UserInput stores them via refs, QueuedMessages is not React.memo-wrapped). * fix(files): improve light mode number token contrast to 4.8x * fix(tasks): annotate raw fetch in createChat for boundary check * fix(tasks): use requestJson with createMothershipChatContract instead of raw fetch * fix(cleanup): remove cancelQueries from onSuccess, remove redundant Scissors className * fix(tasks): guard handleNewTask against concurrent calls via ref lock * fix(drafts): guard save effect against first-render race that wipes file-only drafts On mount the save effect fired before the restore effect's setState calls propagated, so fileAttachments and contexts were still empty. For a draft with files but no text this caused isEmpty() to return true and setDraft to delete the entry. Added isFirstSaveRef to skip the initial run; the restore triggers a re-render that fires the save with the full, correct payload. * fix(files): only show loading skeleton while fetching, relax mongo auth validation - files.tsx: gate loading skeleton on isLoading so navigating to a missing file ID doesn't permanently show the skeleton after load completes - database-tools.ts: remove refine() pairing username+password on mongo connection schema so either field can be provided independently * revert(database-tools): restore mongo username+password paired validation
1 parent 6b0de36 commit d60747d

51 files changed

Lines changed: 641 additions & 273 deletions

File tree

Some content is hidden

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

apps/sim/app/(auth)/login/login-form.tsx

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

33
import { useEffect, useRef, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { Eye, EyeOff, Loader2 } from 'lucide-react'
5+
import { Eye, EyeOff } from 'lucide-react'
66
import Link from 'next/link'
77
import { useRouter, useSearchParams } from 'next/navigation'
88
import {
99
Input,
1010
Label,
11+
Loader,
1112
Modal,
1213
ModalBody,
1314
ModalContent,
@@ -455,7 +456,7 @@ export default function LoginPage({
455456
<button type='submit' disabled={isLoading} className={AUTH_SUBMIT_BTN}>
456457
{isLoading ? (
457458
<span className='flex items-center gap-2'>
458-
<Loader2 className='h-4 w-4 animate-spin' />
459+
<Loader className='h-4 w-4' animate />
459460
Signing in...
460461
</span>
461462
) : (
@@ -570,7 +571,7 @@ export default function LoginPage({
570571
<button type='submit' disabled={isSubmittingReset} className={AUTH_SUBMIT_BTN}>
571572
{isSubmittingReset ? (
572573
<span className='flex items-center gap-2'>
573-
<Loader2 className='h-4 w-4 animate-spin' />
574+
<Loader className='h-4 w-4' animate />
574575
Sending...
575576
</span>
576577
) : (

apps/sim/app/(auth)/oauth/consent/page.tsx

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

33
import { useCallback, useEffect, useState } from 'react'
4-
import { ArrowLeftRight, Loader2 } from 'lucide-react'
4+
import { ArrowLeftRight } from 'lucide-react'
55
import Image from 'next/image'
66
import { useRouter, useSearchParams } from 'next/navigation'
7-
import { Button } from '@/components/emcn'
7+
import { Button, Loader } from '@/components/emcn'
88
import { signOut, useSession } from '@/lib/auth/auth-client'
99
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
1010

@@ -266,7 +266,7 @@ export default function OAuthConsentPage() {
266266
>
267267
{submitting ? (
268268
<span className='flex items-center gap-2'>
269-
<Loader2 className='h-4 w-4 animate-spin' />
269+
<Loader className='h-4 w-4' animate />
270270
Authorizing...
271271
</span>
272272
) : (

apps/sim/app/(auth)/reset-password/reset-password-form.tsx

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

33
import { useState } from 'react'
4-
import { Eye, EyeOff, Loader2 } from 'lucide-react'
5-
import { Input, Label } from '@/components/emcn'
4+
import { Eye, EyeOff } from 'lucide-react'
5+
import { Input, Label, Loader } from '@/components/emcn'
66
import { cn } from '@/lib/core/utils/cn'
77
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
88

@@ -67,7 +67,7 @@ export function RequestResetForm({
6767
<button type='submit' disabled={isSubmitting} className={AUTH_SUBMIT_BTN}>
6868
{isSubmitting ? (
6969
<span className='flex items-center gap-2'>
70-
<Loader2 className='h-4 w-4 animate-spin' />
70+
<Loader className='h-4 w-4' animate />
7171
Sending...
7272
</span>
7373
) : (
@@ -232,7 +232,7 @@ export function SetNewPasswordForm({
232232
<button type='submit' disabled={isSubmitting || !token} className={AUTH_SUBMIT_BTN}>
233233
{isSubmitting ? (
234234
<span className='flex items-center gap-2'>
235-
<Loader2 className='h-4 w-4 animate-spin' />
235+
<Loader className='h-4 w-4' animate />
236236
Resetting...
237237
</span>
238238
) : (

apps/sim/app/(auth)/signup/signup-form.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import { Suspense, useEffect, useMemo, useRef, useState } from 'react'
44
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
55
import { createLogger } from '@sim/logger'
6-
import { Eye, EyeOff, Loader2 } from 'lucide-react'
6+
import { Eye, EyeOff } from 'lucide-react'
77
import Link from 'next/link'
88
import { useRouter, useSearchParams } from 'next/navigation'
99
import { usePostHog } from 'posthog-js/react'
10-
import { Input, Label } from '@/components/emcn'
10+
import { Input, Label, Loader } from '@/components/emcn'
1111
import { client, useSession } from '@/lib/auth/auth-client'
1212
import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env'
1313
import { validateCallbackUrl } from '@/lib/core/security/input-validation'
@@ -530,7 +530,7 @@ function SignupFormContent({ githubAvailable, googleAvailable, isProduction }: S
530530
<button type='submit' disabled={isLoading} className={cn('!mt-6', AUTH_SUBMIT_BTN)}>
531531
{isLoading ? (
532532
<span className='flex items-center gap-2'>
533-
<Loader2 className='h-4 w-4 animate-spin' />
533+
<Loader className='h-4 w-4' animate />
534534
Creating account...
535535
</span>
536536
) : (

apps/sim/app/(auth)/verify/verify-content.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
'use client'
22

33
import { Suspense, useEffect, useState } from 'react'
4-
import { Loader2 } from 'lucide-react'
54
import { useRouter } from 'next/navigation'
6-
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/emcn'
5+
import { InputOTP, InputOTPGroup, InputOTPSlot, Loader } from '@/components/emcn'
76
import { cn } from '@/lib/core/utils/cn'
87
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
98
import { useVerification } from '@/app/(auth)/verify/use-verification'
@@ -118,7 +117,7 @@ function VerificationForm({
118117
>
119118
{isLoading ? (
120119
<span className='flex items-center gap-2'>
121-
<Loader2 className='h-4 w-4 animate-spin' />
120+
<Loader className='h-4 w-4' animate />
122121
Verifying...
123122
</span>
124123
) : (

apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx

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

33
import { useEffect, useMemo, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { Loader2, X } from 'lucide-react'
5+
import { X } from 'lucide-react'
66
import Image from 'next/image'
77
import { useRouter } from 'next/navigation'
8-
import { Modal, ModalClose, ModalContent, ModalTitle, ModalTrigger } from '@/components/emcn'
8+
import {
9+
Loader,
10+
Modal,
11+
ModalClose,
12+
ModalContent,
13+
ModalTitle,
14+
ModalTrigger,
15+
} from '@/components/emcn'
916
import { GithubIcon, GoogleIcon } from '@/components/icons'
1017
import { client } from '@/lib/auth/auth-client'
1118
import { getEnv, isFalsy, isTruthy } from '@/lib/core/config/env'
@@ -142,7 +149,7 @@ export function AuthModal({ children, defaultView = 'login', source }: AuthModal
142149

143150
{!providerStatus ? (
144151
<div className='flex items-center justify-center py-16'>
145-
<Loader2 className='h-5 w-5 animate-spin text-[var(--landing-text-muted)]' />
152+
<Loader className='h-5 w-5 text-[var(--landing-text-muted)]' animate />
146153
</div>
147154
) : (
148155
<>

apps/sim/app/academy/(catalog)/[courseSlug]/components/course-progress.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
'use client'
22

33
import { useEffect, useState } from 'react'
4-
import { CheckCircle2, Circle, ExternalLink, GraduationCap, Loader2 } from 'lucide-react'
4+
import { CheckCircle2, Circle, ExternalLink, GraduationCap } from 'lucide-react'
55
import Link from 'next/link'
6+
import { Loader } from '@/components/emcn'
67
import { getCompletedLessons } from '@/lib/academy/local-progress'
78
import type { Course } from '@/lib/academy/types'
89
import { useSession } from '@/lib/auth/auth-client'
@@ -135,7 +136,7 @@ export function CourseProgress({ course, courseSlug }: CourseProgressProps) {
135136
}
136137
className='flex items-center gap-2 rounded-[5px] bg-[#ECECEC] px-4 py-2 font-[430] text-[#1C1C1C] text-[13px] transition-colors hover:bg-white disabled:opacity-50'
137138
>
138-
{isPending && <Loader2 className='h-3.5 w-3.5 animate-spin' />}
139+
{isPending && <Loader className='h-3.5 w-3.5' animate />}
139140
{isPending ? 'Issuing…' : 'Get certificate'}
140141
</button>
141142
) : (

apps/sim/app/chat/components/auth/email/email-auth.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import { useEffect, useState } from 'react'
44
import { createLogger } from '@sim/logger'
55
import { toError } from '@sim/utils/errors'
6-
import { Loader2 } from 'lucide-react'
7-
import { Input, InputOTP, InputOTPGroup, InputOTPSlot, Label } from '@/components/emcn'
6+
import { Input, InputOTP, InputOTPGroup, InputOTPSlot, Label, Loader } from '@/components/emcn'
87
import { cn } from '@/lib/core/utils/cn'
98
import { quickValidateEmail } from '@/lib/messaging/email/validation'
109
import AuthBackground from '@/app/(auth)/components/auth-background'
@@ -180,7 +179,7 @@ export default function EmailAuth({ identifier }: EmailAuthProps) {
180179
>
181180
{requestOtp.isPending ? (
182181
<span className='flex items-center gap-2'>
183-
<Loader2 className='h-4 w-4 animate-spin' />
182+
<Loader className='h-4 w-4' animate />
184183
Sending Code...
185184
</span>
186185
) : (
@@ -233,7 +232,7 @@ export default function EmailAuth({ identifier }: EmailAuthProps) {
233232
>
234233
{verifyOtp.isPending ? (
235234
<span className='flex items-center gap-2'>
236-
<Loader2 className='h-4 w-4 animate-spin' />
235+
<Loader className='h-4 w-4' animate />
237236
Verifying...
238237
</span>
239238
) : (

apps/sim/app/chat/components/auth/password/password-auth.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import { useState } from 'react'
44
import { createLogger } from '@sim/logger'
55
import { toError } from '@sim/utils/errors'
6-
import { Eye, EyeOff, Loader2 } from 'lucide-react'
7-
import { Input, Label } from '@/components/emcn'
6+
import { Eye, EyeOff } from 'lucide-react'
7+
import { Input, Label, Loader } from '@/components/emcn'
88
import { cn } from '@/lib/core/utils/cn'
99
import AuthBackground from '@/app/(auth)/components/auth-background'
1010
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
@@ -135,7 +135,7 @@ export default function PasswordAuth({ identifier }: PasswordAuthProps) {
135135
>
136136
{authenticate.isPending ? (
137137
<span className='flex items-center gap-2'>
138-
<Loader2 className='h-4 w-4 animate-spin' />
138+
<Loader className='h-4 w-4' animate />
139139
Authenticating...
140140
</span>
141141
) : (

apps/sim/app/chat/components/message/components/file-download.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import { useState } from 'react'
44
import { createLogger } from '@sim/logger'
55
import { sleep } from '@sim/utils/helpers'
6-
import { ArrowDown, Download, Loader2, Music } from 'lucide-react'
7-
import { Button } from '@/components/emcn'
6+
import { ArrowDown, Download, Music } from 'lucide-react'
7+
import { Button, Loader } from '@/components/emcn'
88
import { DefaultFileIcon, getDocumentIcon } from '@/components/icons/document-icons'
99
import type { ChatFile } from '@/app/chat/components/message/message'
1010

@@ -126,7 +126,7 @@ export function ChatFileDownload({ file }: ChatFileDownloadProps) {
126126
</div>
127127
<div className='flex-shrink-0'>
128128
{isDownloading ? (
129-
<Loader2 className='h-3.5 w-3.5 animate-spin' />
129+
<Loader className='h-3.5 w-3.5' animate />
130130
) : (
131131
<ArrowDown
132132
className={`h-3.5 w-3.5 transition-opacity ${isHovered ? 'opacity-100' : 'opacity-0'}`}
@@ -176,7 +176,7 @@ export function ChatFileDownloadAll({ files }: ChatFileDownloadAllProps) {
176176
className='text-[var(--landing-text-muted)] transition-colors hover:bg-[var(--landing-bg-elevated)] disabled:opacity-50'
177177
>
178178
{isDownloading ? (
179-
<Loader2 className='h-3 w-3 animate-spin' strokeWidth={2} />
179+
<Loader className='h-3 w-3' animate />
180180
) : (
181181
<Download className='h-3 w-3' strokeWidth={2} />
182182
)}

0 commit comments

Comments
 (0)