Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { type HTMLAttributes, type ReactNode } from 'react'
import React, { type HTMLAttributes, memo, type ReactNode, useMemo } from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { Tooltip } from '@/components/emcn'
Expand All @@ -23,24 +23,16 @@ export function LinkWithPreview({ href, children }: { href: string; children: Re
)
}

export default function MarkdownRenderer({
content,
customLinkComponent,
}: {
content: string
customLinkComponent?: typeof LinkWithPreview
}) {
const LinkComponent = customLinkComponent || LinkWithPreview
const REMARK_PLUGINS = [remarkGfm]

const customComponents = {
// Paragraph
function createCustomComponents(LinkComponent: typeof LinkWithPreview) {
return {
p: ({ children }: React.HTMLAttributes<HTMLParagraphElement>) => (
<p className='mb-1 font-sans text-base text-gray-800 leading-relaxed last:mb-0 dark:text-gray-200'>
{children}
</p>
),

// Headings
h1: ({ children }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h1 className='mt-10 mb-5 font-sans font-semibold text-2xl text-gray-900 dark:text-gray-100'>
{children}
Expand All @@ -62,7 +54,6 @@ export default function MarkdownRenderer({
</h4>
),

// Lists
ul: ({ children }: React.HTMLAttributes<HTMLUListElement>) => (
<ul
className='mt-1 mb-1 space-y-1 pl-6 font-sans text-gray-800 dark:text-gray-200'
Expand All @@ -89,7 +80,6 @@ export default function MarkdownRenderer({
</li>
),

// Code blocks
pre: ({ children }: HTMLAttributes<HTMLPreElement>) => {
let codeProps: HTMLAttributes<HTMLElement> = {}
let codeContent: ReactNode = children
Expand Down Expand Up @@ -120,7 +110,6 @@ export default function MarkdownRenderer({
)
},

// Inline code
code: ({
inline,
className,
Expand All @@ -144,24 +133,20 @@ export default function MarkdownRenderer({
)
},

// Blockquotes
blockquote: ({ children }: React.HTMLAttributes<HTMLQuoteElement>) => (
<blockquote className='my-4 border-gray-300 border-l-4 py-1 pl-4 font-sans text-gray-700 italic dark:border-gray-600 dark:text-gray-300'>
{children}
</blockquote>
),

// Horizontal rule
hr: () => <hr className='my-8 border-gray-500/[.07] border-t dark:border-gray-400/[.07]' />,

// Links
a: ({ href, children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
<LinkComponent href={href || '#'} {...props}>
{children}
</LinkComponent>
),

// Tables
table: ({ children }: React.TableHTMLAttributes<HTMLTableElement>) => (
<div className='my-4 w-full overflow-x-auto'>
<table className='min-w-full table-auto border border-gray-300 font-sans text-sm dark:border-gray-700'>
Expand Down Expand Up @@ -193,7 +178,6 @@ export default function MarkdownRenderer({
</td>
),

// Images
img: ({ src, alt, ...props }: React.ImgHTMLAttributes<HTMLImageElement>) => (
<img
src={src}
Expand All @@ -203,15 +187,33 @@ export default function MarkdownRenderer({
/>
),
}
}

const DEFAULT_COMPONENTS = createCustomComponents(LinkWithPreview)

const MarkdownRenderer = memo(function MarkdownRenderer({
content,
customLinkComponent,
}: {
content: string
customLinkComponent?: typeof LinkWithPreview
}) {
const components = useMemo(() => {
if (!customLinkComponent) {
return DEFAULT_COMPONENTS
}
return createCustomComponents(customLinkComponent)
}, [customLinkComponent])

// Pre-process content to fix common issues
const processedContent = content.trim()

return (
<div className='space-y-4 break-words font-sans text-[#0D0D0D] text-base leading-relaxed dark:text-gray-100'>
<ReactMarkdown remarkPlugins={[remarkGfm]} components={customComponents}>
<ReactMarkdown remarkPlugins={REMARK_PLUGINS} components={components}>
{processedContent}
</ReactMarkdown>
</div>
)
}
})

export default MarkdownRenderer
9 changes: 8 additions & 1 deletion apps/sim/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { generateBrandedMetadata, generateStructuredData } from '@/lib/branding/
import { PostHogProvider } from '@/app/_shell/providers/posthog-provider'
import '@/app/_styles/globals.css'
import { OneDollarStats } from '@/components/analytics/onedollarstats'
import { isReactGrabEnabled } from '@/lib/core/config/feature-flags'
import { isReactGrabEnabled, isReactScanEnabled } from '@/lib/core/config/feature-flags'
import { HydrationErrorHandler } from '@/app/_shell/hydration-error-handler'
import { QueryProvider } from '@/app/_shell/providers/query-provider'
import { SessionProvider } from '@/app/_shell/providers/session-provider'
Expand Down Expand Up @@ -35,6 +35,13 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
<html lang='en' suppressHydrationWarning>
<head>
{isReactScanEnabled && (
<Script
src='https://unpkg.com/react-scan/dist/auto.global.js'
crossOrigin='anonymous'
strategy='beforeInteractive'
/>
)}
{isReactGrabEnabled && (
<Script
src='https://unpkg.com/react-grab/dist/index.global.js'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import type React from 'react'
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import { createLogger } from '@sim/logger'
import { useParams } from 'next/navigation'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
import { useUserPermissions, type WorkspaceUserPermissions } from '@/hooks/use-user-permissions'
import {
useWorkspacePermissions,
type WorkspacePermissions,
} from '@/hooks/use-workspace-permissions'
import { useNotificationStore } from '@/stores/notifications'
import { useOperationQueueStore } from '@/stores/operation-queue/store'

const logger = createLogger('WorkspacePermissionsProvider')

Expand Down Expand Up @@ -64,8 +64,8 @@ export function WorkspacePermissionsProvider({ children }: WorkspacePermissionsP
// Track whether we've already surfaced an offline notification to avoid duplicates
const [hasShownOfflineNotification, setHasShownOfflineNotification] = useState(false)

// Get operation error state from collaborative workflow
const { hasOperationError } = useCollaborativeWorkflow()
// Get operation error state directly from the store (avoid full useCollaborativeWorkflow subscription)
const hasOperationError = useOperationQueueStore((state) => state.hasOperationError)

const addNotification = useNotificationStore((state) => state.addNotification)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ export const ActionBar = memo(
collaborativeBatchToggleBlockHandles,
} = useCollaborativeWorkflow()
const { activeWorkflowId, setPendingSelection } = useWorkflowRegistry()
const blocks = useWorkflowStore((state) => state.blocks)
const subBlockStore = useSubBlockStore()

const handleDuplicateBlock = useCallback(() => {
const blocks = useWorkflowStore.getState().blocks
const sourceBlock = blocks[blockId]
if (!sourceBlock) return

const newId = crypto.randomUUID()
const newName = getUniqueBlockName(sourceBlock.name, blocks)
const subBlockValues = subBlockStore.workflowValues[activeWorkflowId || '']?.[blockId] || {}
const subBlockValues =
useSubBlockStore.getState().workflowValues[activeWorkflowId || '']?.[blockId] || {}

const { block, subBlockValues: filteredValues } = prepareDuplicateBlockState({
sourceBlock,
Expand All @@ -70,18 +70,8 @@ export const ActionBar = memo(

setPendingSelection([newId])
collaborativeBatchAddBlocks([block], [], {}, {}, { [newId]: filteredValues })
}, [
blockId,
blocks,
activeWorkflowId,
subBlockStore.workflowValues,
collaborativeBatchAddBlocks,
setPendingSelection,
])
}, [blockId, activeWorkflowId, collaborativeBatchAddBlocks, setPendingSelection])

/**
* Optimized single store subscription for all block data
*/
const { isEnabled, horizontalHandles, parentId, parentType } = useWorkflowStore(
useCallback(
(state) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { Code, Tooltip } from '@/components/emcn'

const REMARK_PLUGINS = [remarkGfm]

/**
* Recursively extracts text content from React elements
* @param element - React node to extract text from
Expand Down Expand Up @@ -149,14 +151,12 @@ interface CopilotMarkdownRendererProps {
* Tighter spacing compared to traditional prose for better chat UX
*/
const markdownComponents = {
// Paragraphs - tight spacing, no margin on last
p: ({ children }: React.HTMLAttributes<HTMLParagraphElement>) => (
<p className='mb-1.5 font-base font-season text-[var(--text-primary)] text-sm leading-[1.4] last:mb-0 dark:font-[470]'>
{children}
</p>
),

// Headings - minimal margins for chat context
h1: ({ children }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h1 className='mt-2 mb-1 font-season font-semibold text-[var(--text-primary)] text-base first:mt-0'>
{children}
Expand All @@ -178,7 +178,6 @@ const markdownComponents = {
</h4>
),

// Lists - compact spacing
ul: ({ children }: React.HTMLAttributes<HTMLUListElement>) => (
<ul
className='my-1 space-y-0.5 pl-5 font-base font-season text-[var(--text-primary)] dark:font-[470]'
Expand All @@ -204,7 +203,6 @@ const markdownComponents = {
</li>
),

// Code blocks - handled by CodeBlock component
pre: ({ children }: React.HTMLAttributes<HTMLPreElement>) => {
let codeContent: React.ReactNode = children
let language = 'code'
Expand Down Expand Up @@ -243,7 +241,6 @@ const markdownComponents = {
return <CodeBlock code={actualCodeText} language={language} />
},

// Inline code
code: ({
className,
children,
Expand All @@ -257,7 +254,6 @@ const markdownComponents = {
</code>
),

// Text formatting
strong: ({ children }: React.HTMLAttributes<HTMLElement>) => (
<strong className='font-semibold text-[var(--text-primary)]'>{children}</strong>
),
Expand All @@ -271,22 +267,18 @@ const markdownComponents = {
<i className='text-[var(--text-primary)] italic'>{children}</i>
),

// Blockquote - compact
blockquote: ({ children }: React.HTMLAttributes<HTMLQuoteElement>) => (
<blockquote className='my-1.5 border-[var(--border-1)] border-l-2 py-0.5 pl-3 font-season text-[var(--text-secondary)] text-sm italic'>
{children}
</blockquote>
),

// Horizontal rule
hr: () => <hr className='my-3 border-[var(--divider)] border-t' />,

// Links
a: ({ href, children }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
<LinkWithPreview href={href || '#'}>{children}</LinkWithPreview>
),

// Tables - compact
table: ({ children }: React.TableHTMLAttributes<HTMLTableElement>) => (
<div className='my-2 max-w-full overflow-x-auto'>
<table className='min-w-full table-auto border border-[var(--border-1)] font-season text-xs'>
Expand Down Expand Up @@ -314,7 +306,6 @@ const markdownComponents = {
</td>
),

// Images
img: ({ src, alt, ...props }: React.ImgHTMLAttributes<HTMLImageElement>) => (
<img src={src} alt={alt || 'Image'} className='my-2 h-auto max-w-full rounded-md' {...props} />
),
Expand All @@ -330,7 +321,7 @@ const markdownComponents = {
function CopilotMarkdownRenderer({ content }: CopilotMarkdownRendererProps) {
return (
<div className='max-w-full break-words font-base font-season text-[var(--text-primary)] text-sm leading-[1.4] dark:font-[470] [&_*]:max-w-full [&_a]:break-all [&_code:not(pre_code)]:break-words [&_li]:break-words [&_p]:break-words'>
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
<ReactMarkdown remarkPlugins={REMARK_PLUGINS} components={markdownComponents}>
{content}
</ReactMarkdown>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
export { useCheckpointManagement } from './use-checkpoint-management'
export { useMessageEditing } from './use-message-editing'
export { useMessageFeedback } from './use-message-feedback'
export { useSuccessTimers } from './use-success-timers'
Loading