Skip to content

Commit de3780c

Browse files
committed
Merge remote-tracking branch 'origin/staging' into waleedlatif1/mcp-oauth
# Conflicts: # apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx # apps/sim/hooks/queries/mcp.ts # apps/sim/lib/mcp/client.ts
2 parents 9542541 + 57b9a2f commit de3780c

68 files changed

Lines changed: 9562 additions & 381 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/(landing)/blog/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getNavBlogPosts } from '@/lib/blog/registry'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33
import Footer from '@/app/(landing)/components/footer/footer'
44
import Navbar from '@/app/(landing)/components/navbar/navbar'
5+
import { ScrollToTop } from '@/app/(landing)/components/scroll-to-top'
56

67
export default async function StudioLayout({ children }: { children: React.ReactNode }) {
78
const blogPosts = await getNavBlogPosts()
@@ -29,6 +30,7 @@ export default async function StudioLayout({ children }: { children: React.React
2930

3031
return (
3132
<div className='flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
33+
<ScrollToTop />
3234
<script
3335
type='application/ld+json'
3436
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
import { usePathname } from 'next/navigation'
5+
6+
/**
7+
* Resets window scroll to the top on App Router pathname changes.
8+
*
9+
* Next.js's default scroll handling only brings the new Page element into view,
10+
* which often resolves to "no scroll" inside shared layouts (see vercel/next.js#64435).
11+
* Skipped when a hash anchor is targeted so the browser's native anchor scroll wins.
12+
*/
13+
export function ScrollToTop() {
14+
const pathname = usePathname()
15+
16+
useEffect(() => {
17+
if (window.location.hash) return
18+
window.scrollTo(0, 0)
19+
}, [pathname])
20+
21+
return null
22+
}

apps/sim/app/(landing)/integrations/(shell)/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getNavBlogPosts } from '@/lib/blog/registry'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33
import Footer from '@/app/(landing)/components/footer/footer'
44
import Navbar from '@/app/(landing)/components/navbar/navbar'
5+
import { ScrollToTop } from '@/app/(landing)/components/scroll-to-top'
56

67
export default async function IntegrationsLayout({ children }: { children: React.ReactNode }) {
78
const blogPosts = await getNavBlogPosts()
@@ -29,6 +30,7 @@ export default async function IntegrationsLayout({ children }: { children: React
2930

3031
return (
3132
<div className='dark flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
33+
<ScrollToTop />
3234
<script
3335
type='application/ld+json'
3436
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}

apps/sim/app/(landing)/models/(shell)/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { getNavBlogPosts } from '@/lib/blog/registry'
22
import { SITE_URL } from '@/lib/core/utils/urls'
33
import Footer from '@/app/(landing)/components/footer/footer'
44
import Navbar from '@/app/(landing)/components/navbar/navbar'
5+
import { ScrollToTop } from '@/app/(landing)/components/scroll-to-top'
56

67
export default async function ModelsLayout({ children }: { children: React.ReactNode }) {
78
const blogPosts = await getNavBlogPosts()
@@ -24,6 +25,7 @@ export default async function ModelsLayout({ children }: { children: React.React
2425

2526
return (
2627
<div className='dark flex min-h-screen flex-col bg-[var(--landing-bg)] font-[430] font-season text-[var(--landing-text)]'>
28+
<ScrollToTop />
2729
<script
2830
type='application/ld+json'
2931
dangerouslySetInnerHTML={{ __html: JSON.stringify(orgJsonLd) }}

apps/sim/app/api/knowledge/utils.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,32 @@ vi.mock('@sim/db', async () => {
116116
},
117117
}
118118
},
119+
innerJoin() {
120+
// document × knowledge_base context JOIN — return the first kb and
121+
// doc row merged (covers processDocumentAsync's prefetch).
122+
return {
123+
leftJoin: () => ({
124+
where: () => ({
125+
limit: (n: number) =>
126+
Promise.resolve(
127+
kbRows.length > 0 && docRows.length > 0
128+
? [
129+
{ ...kbRows[0], ...docRows[0], billedAccountUserId: 'billing-user-1' },
130+
].slice(0, n)
131+
: []
132+
),
133+
}),
134+
}),
135+
where: () => ({
136+
limit: (n: number) =>
137+
Promise.resolve(
138+
kbRows.length > 0 && docRows.length > 0
139+
? [{ ...kbRows[0], ...docRows[0] }].slice(0, n)
140+
: []
141+
),
142+
}),
143+
}
144+
},
119145
}
120146
},
121147
}

apps/sim/app/api/table/[tableId]/dispatches/route.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { parseRequest } from '@/lib/api/server'
55
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
66
import { generateRequestId } from '@/lib/core/utils/request'
77
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
8-
import { countRunningCells, listActiveDispatches } from '@/lib/table/dispatcher'
8+
import { countActiveRunCells, listActiveDispatches } from '@/lib/table/dispatcher'
99
import { accessError, checkAccess } from '@/app/api/table/utils'
1010

1111
const logger = createLogger('TableDispatchesAPI')
@@ -37,10 +37,8 @@ export const GET = withRouteHandler(async (request: NextRequest, { params }: Rou
3737
const result = await checkAccess(tableId, authResult.userId, 'read')
3838
if (!result.ok) return accessError(result, requestId, tableId)
3939

40-
const [rows, running] = await Promise.all([
41-
listActiveDispatches(tableId),
42-
countRunningCells(tableId),
43-
])
40+
const rows = await listActiveDispatches(tableId)
41+
const running = await countActiveRunCells(tableId, rows)
4442
const dispatches: ActiveDispatch[] = rows.map((r) => ({
4543
id: r.id,
4644
status: r.status as 'pending' | 'dispatching',

apps/sim/app/api/tools/tts/route.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,17 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
3535
)
3636
if (!parsed.success) return parsed.response
3737

38-
const { text, voiceId, apiKey, modelId, workspaceId, workflowId, executionId } =
39-
parsed.data.body
38+
const {
39+
text,
40+
voiceId,
41+
apiKey,
42+
modelId,
43+
stability,
44+
similarityBoost,
45+
workspaceId,
46+
workflowId,
47+
executionId,
48+
} = parsed.data.body
4049

4150
const voiceIdValidation = validateAlphanumericId(voiceId, 'voiceId', 255)
4251
if (!voiceIdValidation.isValid) {
@@ -57,6 +66,14 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
5766

5867
const endpoint = `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`
5968

69+
const hasVoiceSetting = stability !== undefined || similarityBoost !== undefined
70+
const voiceSettings = hasVoiceSetting
71+
? {
72+
stability: stability ?? 0.5,
73+
similarity_boost: similarityBoost ?? 0.75,
74+
}
75+
: undefined
76+
6077
const response = await fetch(endpoint, {
6178
method: 'POST',
6279
headers: {
@@ -67,6 +84,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
6784
body: JSON.stringify({
6885
text,
6986
model_id: modelId,
87+
...(voiceSettings ? { voice_settings: voiceSettings } : {}),
7088
}),
7189
signal: AbortSignal.timeout(DEFAULT_EXECUTION_TIMEOUT_MS),
7290
})

apps/sim/app/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const metadata: Metadata = {
3131
locale: 'en_US',
3232
images: [
3333
{
34-
url: '/logo/426-240/primary/small.png',
34+
url: '/logo/426-240/reverse/small.png',
3535
width: 2130,
3636
height: 1200,
3737
alt: 'Sim — The AI Workspace for Teams',
@@ -47,7 +47,7 @@ export const metadata: Metadata = {
4747
description:
4848
'Sim is the open-source AI workspace where teams build, deploy, and manage AI agents. Connect 1,000+ integrations and every major LLM to create agents that automate real work.',
4949
images: {
50-
url: '/logo/426-240/primary/small.png',
50+
url: '/logo/426-240/reverse/small.png',
5151
alt: 'Sim — The AI Workspace for Teams',
5252
},
5353
},

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -296,35 +296,44 @@ const TYPEWRITER_MS_PER_CHAR = 15
296296
* value statically — animation fires only for subsequent updates, which in
297297
* practice means SSE-driven workflow completions arriving via
298298
* `useTableEventStream → applyCell()`.
299+
*
300+
* rAF-driven (not `setInterval`) so concurrent reveals batch into one
301+
* render/paint per frame instead of O(cells) uncoordinated reflows; reveal
302+
* length is elapsed-time based so dropped frames catch up rather than slow.
299303
*/
300304
function useTypewriter(text: string | null): string | null {
301305
const [revealed, setRevealed] = useState<string | null>(text)
302-
const isFirstRunRef = useRef(true)
303306
const prevTextRef = useRef<string | null>(text)
307+
const mountedRef = useRef(false)
308+
const animateRef = useRef(false)
304309

305-
useEffect(() => {
306-
if (isFirstRunRef.current) {
307-
isFirstRunRef.current = false
308-
prevTextRef.current = text
309-
setRevealed(text)
310-
return
311-
}
312-
if (prevTextRef.current === text) return
310+
// Reset synchronously during render when `text` changes (not on first mount)
311+
// so no frame ever shows the full new value before the animation begins —
312+
// an effect-based reset lands one frame late and flashes the whole text.
313+
if (prevTextRef.current !== text) {
313314
prevTextRef.current = text
315+
const animate = mountedRef.current && text !== null && text.length > 0
316+
animateRef.current = animate
317+
setRevealed(animate ? '' : text)
318+
}
314319

315-
if (text === null || text.length === 0) {
316-
setRevealed(text)
317-
return
318-
}
320+
useEffect(() => {
321+
mountedRef.current = true
322+
}, [])
319323

320-
setRevealed('')
321-
let i = 0
322-
const id = window.setInterval(() => {
323-
i++
324-
setRevealed(text.slice(0, i))
325-
if (i >= text.length) window.clearInterval(id)
326-
}, TYPEWRITER_MS_PER_CHAR)
327-
return () => window.clearInterval(id)
324+
useEffect(() => {
325+
if (!animateRef.current) return
326+
animateRef.current = false
327+
const full = text as string
328+
const start = performance.now()
329+
let raf = 0
330+
const tick = (now: number) => {
331+
const chars = Math.min(full.length, Math.floor((now - start) / TYPEWRITER_MS_PER_CHAR))
332+
setRevealed(full.slice(0, chars))
333+
if (chars < full.length) raf = requestAnimationFrame(tick)
334+
}
335+
raf = requestAnimationFrame(tick)
336+
return () => cancelAnimationFrame(raf)
328337
}, [text])
329338

330339
return revealed

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/hooks/use-table-event-stream.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@ export function useTableEventStream({
7373
let lastEventId = loadPointer(tableId)
7474
let reconnectAttempt = 0
7575

76-
const updateRunStateCounters = (
77-
rowId: string,
78-
wasInFlight: boolean,
79-
isInFlight: boolean
80-
): void => {
76+
// Keeps the per-row gutter (`runningByRowId`) live between dispatch events.
77+
// `runningCellCount` (the "X running" badge) is NOT touched here — it's the
78+
// server's dispatch-scope count, seeded optimistically on click and
79+
// re-synced by `applyDispatch` on every window, so live matches reload.
80+
const updateRunningByRow = (rowId: string, wasInFlight: boolean, isInFlight: boolean): void => {
8181
if (wasInFlight === isInFlight) return
8282
const delta = isInFlight ? 1 : -1
8383
queryClient.setQueryData<TableRunState>(tableKeys.activeDispatches(tableId), (prev) => {
@@ -87,11 +87,7 @@ export function useTableEventStream({
8787
const nextByRow = { ...prev.runningByRowId }
8888
if (nextForRow === 0) delete nextByRow[rowId]
8989
else nextByRow[rowId] = nextForRow
90-
return {
91-
...prev,
92-
runningCellCount: Math.max(0, prev.runningCellCount + delta),
93-
runningByRowId: nextByRow,
94-
}
90+
return { ...prev, runningByRowId: nextByRow }
9591
})
9692
}
9793

@@ -145,11 +141,7 @@ export function useTableEventStream({
145141
queryKey: tableKeys.activeDispatches(tableId),
146142
})
147143
} else {
148-
updateRunStateCounters(
149-
rowId,
150-
wasInFlight,
151-
isExecInFlight({ status } as RowExecutionMetadata)
152-
)
144+
updateRunningByRow(rowId, wasInFlight, isExecInFlight({ status } as RowExecutionMetadata))
153145
}
154146
}
155147

@@ -195,6 +187,11 @@ export function useTableEventStream({
195187
merged[idx] = next
196188
return { ...base, dispatches: merged }
197189
})
190+
// The dispatcher emits this once per window (after the window's cells
191+
// finish + the cursor advances) and on completion. Re-sync the
192+
// dispatch-scope `runningCellCount` from the server so the badge steps
193+
// down per window and matches a reload exactly.
194+
void queryClient.invalidateQueries({ queryKey: tableKeys.activeDispatches(tableId) })
198195
}
199196

200197
const handlePrune = (payload: PrunedEvent): void => {

0 commit comments

Comments
 (0)