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
14 changes: 14 additions & 0 deletions .claude/rules/sim-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ import { Dashboard, Sidebar } from '@/app/workspace/[workspaceId]/logs/component
import { Dashboard } from '@/app/workspace/[workspaceId]/logs/components/dashboard/dashboard'
```

## Code-splitting through barrels

When you `lazy(() => import(...))` a component to keep it out of a route's initial bundle, import the **deep module path** (`./components/foo/foo`), never the barrel — and **delete the now-dead barrel re-export** of that component. This app has no `"sideEffects": false` in `apps/sim/package.json`, so when any sibling still imports that barrel, webpack can conservatively keep the barrel's re-export edge to the heavy module. A leftover `export { Foo } from './foo'` line can therefore drag `Foo` (and its transitive deps) back into the initial chunk and silently defeat the split. Removing the dead re-export is the guaranteed fix; verify with a production bundle diff, not by eyeballing the `lazy()` call.

```typescript
// ✓ Good — deep lazy import + no barrel edge left behind
const MothershipView = lazy(() =>
import('./components/mothership-view/mothership-view').then((m) => ({ default: m.MothershipView }))
)
// (and remove `export { MothershipView } from './mothership-view'` from components/index.ts)
```

Wrap the lazy component in a **local `<Suspense>`** so its suspension resolves at the nearest boundary instead of bubbling to the page-level fallback (which would flash the whole route). `React.lazy(memo(forwardRef(...)))` forwards a DOM `ref` correctly in React 19 — but during the fallback window `ref.current` is `null`, so every consumer must guard it (`if (!el) return` / `el?.`).

## No Re-exports

Do not re-export from non-barrel files. Import directly from the source.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export {
MothershipResourcesProvider,
useMothershipResources,
} from './mothership-resources-context'
export { MothershipView } from './mothership-view'
export { QueuedMessages } from './queued-messages'
export { SuggestedActions } from './suggested-actions'
export { UserInput, type UserInputHandle } from './user-input'
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ export function PromptEditor({
return <span>{displayText}</span>
}

const contextByLabel = new Map<string, (typeof contexts)[number]>()
for (const c of contexts) {
if (!contextByLabel.has(c.label)) contextByLabel.set(c.label, c)
}

const elements: React.ReactNode[] = []
let lastIndex = 0
for (let i = 0; i < ranges.length; i++) {
Expand All @@ -133,7 +138,7 @@ export function PromptEditor({
}

const mentionLabel = stripMentionTrigger(range.token)
const matchingCtx = contexts.find((c) => c.label === mentionLabel)
const matchingCtx = contextByLabel.get(mentionLabel)

const mentionIconNode = matchingCtx ? (
<ContextMentionIcon
Expand Down
40 changes: 27 additions & 13 deletions apps/sim/app/workspace/[workspaceId]/home/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import {
type Dispatch,
lazy,
type SetStateAction,
Suspense,
useCallback,
useEffect,
useMemo,
Expand Down Expand Up @@ -49,7 +51,6 @@ import {
CreditsChip,
MothershipChat,
MothershipResourcesProvider,
MothershipView,
SuggestedActions,
UserInput,
type UserInputHandle,
Expand All @@ -59,6 +60,17 @@ import type { FileAttachmentForApi, MothershipResource, MothershipResourceType }

const logger = createLogger('Home')

/**
* The resource preview panel pulls in the file-viewer stack (rich-markdown
* editor, CSV/PDF viewers). It only renders once a chat has messages, so it is
* code-split out of the initial `/chat` bundle and loaded on demand.
*/
const MothershipView = lazy(() =>
import('./components/mothership-view/mothership-view').then((m) => ({
default: m.MothershipView,
}))
)

interface HomeProps {
chatId?: string
userName?: string
Expand Down Expand Up @@ -491,18 +503,20 @@ export function Home({ chatId, userName, userId }: HomeProps) {
reorderResources={reorderResources}
collapseResource={collapseResource}
>
<MothershipView
ref={mothershipRef}
workspaceId={workspaceId}
chatId={resolvedChatId}
resources={resources}
activeResourceId={activeResourceId}
isCollapsed={isResourceCollapsed}
previewSession={previewSession}
isAgentResponding={isSending}
genericResourceData={genericResourceData ?? undefined}
className={skipResourceTransition ? '!transition-none' : undefined}
/>
<Suspense fallback={null}>
<MothershipView
ref={mothershipRef}
workspaceId={workspaceId}
chatId={resolvedChatId}
resources={resources}
activeResourceId={activeResourceId}
isCollapsed={isResourceCollapsed}
previewSession={previewSession}
isAgentResponding={isSending}
genericResourceData={genericResourceData ?? undefined}
className={skipResourceTransition ? '!transition-none' : undefined}
/>
</Suspense>
</MothershipResourcesProvider>

{isResourceCollapsed && (
Expand Down
Loading