From 0c3cb4b0adefa0dd1611bc0e4faf7e96c4c902d5 Mon Sep 17 00:00:00 2001 From: Theodore Li Date: Tue, 7 Apr 2026 16:11:31 -0700 Subject: [PATCH 1/2] v0.6.29: login improvements, posthog telemetry (#4026) * feat(posthog): Add tracking on mothership abort (#4023) Co-authored-by: Theodore Li * fix(login): fix captcha headers for manual login (#4025) * fix(signup): fix turnstile key loading * fix(login): fix captcha header passing * Catch user already exists, remove login form captcha --- apps/sim/app/workspace/[workspaceId]/home/home.tsx | 8 ++++++++ .../w/[workflowId]/components/panel/panel.tsx | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index 5680f8efdd9..e1f73204fbf 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -209,6 +209,14 @@ export function Home({ chatId }: HomeProps = {}) { void stopGeneration().catch(() => {}) }, [stopGeneration, workspaceId]) + const handleStopGeneration = useCallback(() => { + captureEvent(posthogRef.current, 'task_generation_aborted', { + workspace_id: workspaceId, + view: 'mothership', + }) + stopGeneration() + }, [stopGeneration, workspaceId]) + const handleSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index e881ed300dd..5cddffddc10 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -400,6 +400,14 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel copilotStopGeneration() }, [copilotStopGeneration, workspaceId]) + const handleCopilotStopGeneration = useCallback(() => { + captureEvent(posthogRef.current, 'task_generation_aborted', { + workspace_id: workspaceId, + view: 'copilot', + }) + copilotStopGeneration() + }, [copilotStopGeneration, workspaceId]) + const handleCopilotSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() From 904ab7d0095d7a073cb59a7fb53d01379d261f14 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 15 Apr 2026 23:30:02 -0700 Subject: [PATCH 2/2] fix(ui): posthog guard, dynamic import loading, compact variant, rebase cleanup --- .../collaboration/collaboration.tsx | 5 +++-- .../components/features/features.tsx | 5 +++-- .../components/footer/footer-cta.tsx | 5 +++-- .../app/(landing)/components/hero/hero.tsx | 15 +++++++------ .../landing-preview-panel.tsx | 5 +++-- .../(landing)/components/navbar/navbar.tsx | 5 +++-- .../(landing)/components/pricing/pricing.tsx | 15 +++++++------ .../app/_shell/providers/session-provider.tsx | 1 + .../queued-messages/queued-messages.tsx | 21 ++++++++++--------- .../user-message-content.tsx | 17 +++++++++------ .../app/workspace/[workspaceId]/home/home.tsx | 8 ------- .../w/[workflowId]/components/panel/panel.tsx | 8 ------- 12 files changed, 56 insertions(+), 54 deletions(-) diff --git a/apps/sim/app/(landing)/components/collaboration/collaboration.tsx b/apps/sim/app/(landing)/components/collaboration/collaboration.tsx index d7b8cff120b..11e912d975d 100644 --- a/apps/sim/app/(landing)/components/collaboration/collaboration.tsx +++ b/apps/sim/app/(landing)/components/collaboration/collaboration.tsx @@ -7,8 +7,9 @@ import Link from 'next/link' import { Badge } from '@/components/emcn' import { trackLandingCta } from '@/app/(landing)/landing-analytics' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) interface DotGridProps { diff --git a/apps/sim/app/(landing)/components/features/features.tsx b/apps/sim/app/(landing)/components/features/features.tsx index f9ab0d6d6c0..e1395305e78 100644 --- a/apps/sim/app/(landing)/components/features/features.tsx +++ b/apps/sim/app/(landing)/components/features/features.tsx @@ -8,8 +8,9 @@ import { Badge } from '@/components/emcn' import { FeaturesPreview } from '@/app/(landing)/components/features/components/features-preview' import { trackLandingCta } from '@/app/(landing)/landing-analytics' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) function hexToRgba(hex: string, alpha: number): string { diff --git a/apps/sim/app/(landing)/components/footer/footer-cta.tsx b/apps/sim/app/(landing)/components/footer/footer-cta.tsx index ff98ff7e156..3ec991880db 100644 --- a/apps/sim/app/(landing)/components/footer/footer-cta.tsx +++ b/apps/sim/app/(landing)/components/footer/footer-cta.tsx @@ -9,8 +9,9 @@ import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/com import { trackLandingCta } from '@/app/(landing)/landing-analytics' import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) const MAX_HEIGHT = 120 diff --git a/apps/sim/app/(landing)/components/hero/hero.tsx b/apps/sim/app/(landing)/components/hero/hero.tsx index 62b62b4e73d..dc1343149ba 100644 --- a/apps/sim/app/(landing)/components/hero/hero.tsx +++ b/apps/sim/app/(landing)/components/hero/hero.tsx @@ -4,14 +4,17 @@ import dynamic from 'next/dynamic' import { cn } from '@/lib/core/utils/cn' import { trackLandingCta } from '@/app/(landing)/landing-analytics' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) -const DemoRequestModal = dynamic(() => - import('@/app/(landing)/components/demo-request/demo-request-modal').then( - (m) => m.DemoRequestModal - ) +const DemoRequestModal = dynamic( + () => + import('@/app/(landing)/components/demo-request/demo-request-modal').then( + (m) => m.DemoRequestModal + ), + { loading: () => null } ) const LandingPreview = dynamic( diff --git a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx index 94e39cb27b3..5e77354fc82 100644 --- a/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx +++ b/apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx @@ -21,8 +21,9 @@ import { } from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data' import { trackLandingCta } from '@/app/(landing)/landing-analytics' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) type PanelTab = 'copilot' | 'editor' diff --git a/apps/sim/app/(landing)/components/navbar/navbar.tsx b/apps/sim/app/(landing)/components/navbar/navbar.tsx index da1e114f076..4ee2fdb2aca 100644 --- a/apps/sim/app/(landing)/components/navbar/navbar.tsx +++ b/apps/sim/app/(landing)/components/navbar/navbar.tsx @@ -17,8 +17,9 @@ import { GitHubStars } from '@/app/(landing)/components/navbar/components/github import { trackLandingCta } from '@/app/(landing)/landing-analytics' import { getBrandConfig } from '@/ee/whitelabeling' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) type DropdownId = 'docs' | 'blog' | null diff --git a/apps/sim/app/(landing)/components/pricing/pricing.tsx b/apps/sim/app/(landing)/components/pricing/pricing.tsx index 3150e7213a1..48a1bf4dd8c 100644 --- a/apps/sim/app/(landing)/components/pricing/pricing.tsx +++ b/apps/sim/app/(landing)/components/pricing/pricing.tsx @@ -4,14 +4,17 @@ import dynamic from 'next/dynamic' import { Badge } from '@/components/emcn' import { trackLandingCta } from '@/app/(landing)/landing-analytics' -const AuthModal = dynamic(() => - import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal) +const AuthModal = dynamic( + () => import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal), + { loading: () => null } ) -const DemoRequestModal = dynamic(() => - import('@/app/(landing)/components/demo-request/demo-request-modal').then( - (m) => m.DemoRequestModal - ) +const DemoRequestModal = dynamic( + () => + import('@/app/(landing)/components/demo-request/demo-request-modal').then( + (m) => m.DemoRequestModal + ), + { loading: () => null } ) interface PricingTier { diff --git a/apps/sim/app/_shell/providers/session-provider.tsx b/apps/sim/app/_shell/providers/session-provider.tsx index ecd1211d720..6d77ee79c82 100644 --- a/apps/sim/app/_shell/providers/session-provider.tsx +++ b/apps/sim/app/_shell/providers/session-provider.tsx @@ -94,6 +94,7 @@ export function SessionProvider({ children }: { children: React.ReactNode }) { }) if ( typeof posthog.startSessionRecording === 'function' && + typeof posthog.sessionRecordingStarted === 'function' && !posthog.sessionRecordingStarted() ) { posthog.startSessionRecording() diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx index 55c0478727b..0773aea4c43 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useRef, useState } from 'react' import { ArrowUp, ChevronDown, ChevronRight, Paperclip, Pencil, Trash2 } from 'lucide-react' import { Tooltip } from '@/components/emcn' import { UserMessageContent } from '@/app/workspace/[workspaceId]/home/components/user-message-content' @@ -17,22 +17,23 @@ interface QueuedMessagesProps { export function QueuedMessages({ messageQueue, onRemove, onSendNow, onEdit }: QueuedMessagesProps) { const [isExpanded, setIsExpanded] = useState(true) - const containerRef = useRef(null) const [isNarrow, setIsNarrow] = useState(false) + const roRef = useRef(null) - const hasMessages = messageQueue.length > 0 - - useEffect(() => { - const el = containerRef.current + const containerRef = useCallback((el: HTMLDivElement | null) => { + if (roRef.current) { + roRef.current.disconnect() + roRef.current = null + } if (!el) return const ro = new ResizeObserver((entries) => { setIsNarrow(entries[0].contentRect.width < NARROW_WIDTH_PX) }) ro.observe(el) - return () => ro.disconnect() - }, [hasMessages]) + roRef.current = ro + }, []) - if (!hasMessages) return null + if (messageQueue.length === 0) return null return (
diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx index bbba6b4bd72..5e58f5ca6f4 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/user-message-content/user-message-content.tsx @@ -10,12 +10,17 @@ import { useWorkflows } from '@/hooks/queries/workflows' const USER_MESSAGE_CLASSES = 'whitespace-pre-wrap break-words [overflow-wrap:anywhere] font-[430] font-[family-name:var(--font-inter)] text-base text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased' +const COMPACT_CLASSES = + 'truncate text-small leading-[20px] font-[430] font-[family-name:var(--font-inter)] text-[var(--text-primary)] tracking-[0] antialiased' + interface UserMessageContentProps { content: string contexts?: ChatMessageContext[] className?: string /** When true, render mentions as plain inline text (no icon/pill) so truncation flows naturally. */ plainMentions?: boolean + /** Use compact single-line layout with truncation. */ + compact?: boolean } function escapeRegex(str: string): string { @@ -73,15 +78,15 @@ export function UserMessageContent({ contexts, className, plainMentions = false, + compact = false, }: UserMessageContentProps) { const trimmed = content.trim() - const classes = cn(USER_MESSAGE_CLASSES, className) + const classes = cn(compact ? COMPACT_CLASSES : USER_MESSAGE_CLASSES, className) - if (!contexts || contexts.length === 0) { - return

{trimmed}

- } - - const ranges = computeMentionRanges(content, contexts) + const ranges = useMemo( + () => (contexts && contexts.length > 0 ? computeMentionRanges(content, contexts) : []), + [content, contexts] + ) if (ranges.length === 0) { return

{trimmed}

diff --git a/apps/sim/app/workspace/[workspaceId]/home/home.tsx b/apps/sim/app/workspace/[workspaceId]/home/home.tsx index e1f73204fbf..5680f8efdd9 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/home.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/home.tsx @@ -209,14 +209,6 @@ export function Home({ chatId }: HomeProps = {}) { void stopGeneration().catch(() => {}) }, [stopGeneration, workspaceId]) - const handleStopGeneration = useCallback(() => { - captureEvent(posthogRef.current, 'task_generation_aborted', { - workspace_id: workspaceId, - view: 'mothership', - }) - stopGeneration() - }, [stopGeneration, workspaceId]) - const handleSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index 5cddffddc10..e881ed300dd 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -400,14 +400,6 @@ export const Panel = memo(function Panel({ workspaceId: propWorkspaceId }: Panel copilotStopGeneration() }, [copilotStopGeneration, workspaceId]) - const handleCopilotStopGeneration = useCallback(() => { - captureEvent(posthogRef.current, 'task_generation_aborted', { - workspace_id: workspaceId, - view: 'copilot', - }) - copilotStopGeneration() - }, [copilotStopGeneration, workspaceId]) - const handleCopilotSubmit = useCallback( (text: string, fileAttachments?: FileAttachmentForApi[], contexts?: ChatContext[]) => { const trimmed = text.trim()