From ed19fed0cadb3c1153ff48da4c77d494b4ddeb05 Mon Sep 17 00:00:00 2001 From: Waleed Date: Tue, 7 Apr 2026 08:42:49 -0700 Subject: [PATCH 01/13] fix(blog): stack featured posts vertically on mobile to prevent horizontal overflow (#4012) --- apps/sim/app/(landing)/blog/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/(landing)/blog/page.tsx b/apps/sim/app/(landing)/blog/page.tsx index d938d014f97..9a43a935eab 100644 --- a/apps/sim/app/(landing)/blog/page.tsx +++ b/apps/sim/app/(landing)/blog/page.tsx @@ -81,12 +81,12 @@ export default async function BlogIndex({ {/* Featured posts */} {featured.length > 0 && ( <> -
+
{featured.map((p, index) => (
Date: Wed, 8 Apr 2026 00:46:18 +0900 Subject: [PATCH 02/13] fix(sso): default tokenEndpointAuthentication to client_secret_post (#3627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(sso): default tokenEndpointAuthentication to client_secret_post better-auth's SSO plugin does not URL-encode credentials before Base64 encoding in client_secret_basic mode (RFC 6749 §2.3.1). When the client secret contains special characters (+, =, /), OIDC providers decode them incorrectly, causing invalid_client errors. Default to client_secret_post when tokenEndpointAuthentication is not explicitly set to avoid this upstream encoding issue. Fixes #3626 * fix(sso): use nullish coalescing and add env var for tokenEndpointAuthentication - Use ?? instead of || for semantic correctness - Add SSO_OIDC_TOKEN_ENDPOINT_AUTH env var so users can explicitly set client_secret_basic when their provider requires it * docs(sso): add SSO_OIDC_TOKEN_ENDPOINT_AUTH to script usage comment Signed-off-by: Mini Jeong * fix(sso): validate SSO_OIDC_TOKEN_ENDPOINT_AUTH env var value Replace unsafe `as` type cast with runtime validation to ensure only 'client_secret_post' or 'client_secret_basic' are accepted. Invalid values (typos, empty strings) now fall back to undefined, letting the downstream ?? fallback apply correctly. Signed-off-by: Mini Jeong --------- Signed-off-by: Mini Jeong --- packages/db/scripts/register-sso-provider.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/db/scripts/register-sso-provider.ts b/packages/db/scripts/register-sso-provider.ts index 07d0ff7684d..ed00894efce 100644 --- a/packages/db/scripts/register-sso-provider.ts +++ b/packages/db/scripts/register-sso-provider.ts @@ -21,6 +21,7 @@ * SSO_OIDC_CLIENT_ID=your_client_id * SSO_OIDC_CLIENT_SECRET=your_client_secret * SSO_OIDC_SCOPES=openid,profile,email (optional) + * SSO_OIDC_TOKEN_ENDPOINT_AUTH=client_secret_post|client_secret_basic (optional, defaults to client_secret_post) * * SAML Providers: * SSO_SAML_ENTRY_POINT=https://your-idp/sso @@ -215,6 +216,11 @@ function buildSSOConfigFromEnv(): SSOProviderConfig | null { pkce: process.env.SSO_OIDC_PKCE !== 'false', authorizationEndpoint: process.env.SSO_OIDC_AUTHORIZATION_ENDPOINT, tokenEndpoint: process.env.SSO_OIDC_TOKEN_ENDPOINT, + tokenEndpointAuthentication: + process.env.SSO_OIDC_TOKEN_ENDPOINT_AUTH === 'client_secret_post' || + process.env.SSO_OIDC_TOKEN_ENDPOINT_AUTH === 'client_secret_basic' + ? process.env.SSO_OIDC_TOKEN_ENDPOINT_AUTH + : undefined, userInfoEndpoint: process.env.SSO_OIDC_USERINFO_ENDPOINT, jwksEndpoint: process.env.SSO_OIDC_JWKS_ENDPOINT, discoveryEndpoint: @@ -507,7 +513,11 @@ async function registerSSOProvider(): Promise { clientSecret: ssoConfig.oidcConfig.clientSecret, authorizationEndpoint: ssoConfig.oidcConfig.authorizationEndpoint, tokenEndpoint: ssoConfig.oidcConfig.tokenEndpoint, - tokenEndpointAuthentication: ssoConfig.oidcConfig.tokenEndpointAuthentication, + // Default to client_secret_post: better-auth sends client_secret_basic + // credentials without URL-encoding per RFC 6749 §2.3.1, so '+' in secrets + // is decoded as space by OIDC providers, causing invalid_client errors. + tokenEndpointAuthentication: + ssoConfig.oidcConfig.tokenEndpointAuthentication ?? 'client_secret_post', jwksEndpoint: ssoConfig.oidcConfig.jwksEndpoint, pkce: ssoConfig.oidcConfig.pkce, discoveryEndpoint: From d5bea5f266daafd7d35efd8812fd55cf60b6ca40 Mon Sep 17 00:00:00 2001 From: chaoliang yan Date: Wed, 8 Apr 2026 01:49:43 +1000 Subject: [PATCH 03/13] fix(table): escape LIKE wildcards in $contains filter values (#3949) The $contains filter operator builds an ILIKE pattern but does not escape LIKE wildcard characters (%, _) in user-provided values. This causes incorrect, over-broad query results when the search value contains these characters. For example, filtering with { name: { $contains: "100%" } } matches any row where name contains "100" followed by anything, not just the literal "100%". Escape %, _, and \ in the value before interpolating into the ILIKE pattern so that they match literally. Co-authored-by: Waleed Co-authored-by: lawrence3699 --- apps/sim/lib/table/sql.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/sim/lib/table/sql.ts b/apps/sim/lib/table/sql.ts index e21d6c68968..2fea3559b20 100644 --- a/apps/sim/lib/table/sql.ts +++ b/apps/sim/lib/table/sql.ts @@ -322,10 +322,15 @@ function buildComparisonClause( return sql`(${sql.raw(`${tableName}.data->>'${escapedField}'`)})::numeric ${sql.raw(operator)} ${value}` } +/** Escapes LIKE/ILIKE wildcard characters so they match literally */ +function escapeLikePattern(value: string): string { + return value.replace(/[\\%_]/g, '\\$&') +} + /** Builds case-insensitive pattern match: `data->>'field' ILIKE '%value%'` */ function buildContainsClause(tableName: string, field: string, value: string): SQL { const escapedField = field.replace(/'/g, "''") - return sql`${sql.raw(`${tableName}.data->>'${escapedField}'`)} ILIKE ${`%${value}%`}` + return sql`${sql.raw(`${tableName}.data->>'${escapedField}'`)} ILIKE ${`%${escapeLikePattern(value)}%`}` } /** From 0b439ecda618589765de6f4733aa1733c4018983 Mon Sep 17 00:00:00 2001 From: Waleed Date: Tue, 7 Apr 2026 10:30:13 -0700 Subject: [PATCH 04/13] chore(stores): remove unused exports and dead code from zustand stores (#4014) --- apps/sim/stores/folders/index.ts | 2 +- apps/sim/stores/folders/store.ts | 3 --- apps/sim/stores/folders/types.ts | 10 ---------- apps/sim/stores/notifications/index.ts | 6 +----- apps/sim/stores/terminal/console/index.ts | 10 +--------- apps/sim/stores/terminal/index.ts | 6 ------ apps/sim/stores/variables/index.ts | 1 - apps/sim/stores/variables/modal.ts | 14 +------------- 8 files changed, 4 insertions(+), 48 deletions(-) diff --git a/apps/sim/stores/folders/index.ts b/apps/sim/stores/folders/index.ts index 64c2a62a0b9..5b58f0404c2 100644 --- a/apps/sim/stores/folders/index.ts +++ b/apps/sim/stores/folders/index.ts @@ -1,2 +1,2 @@ export { useFolderStore, useIsFolderSelected, useIsWorkflowSelected } from './store' -export type { FolderTreeNode, Workflow, WorkflowFolder } from './types' +export type { FolderTreeNode, WorkflowFolder } from './types' diff --git a/apps/sim/stores/folders/store.ts b/apps/sim/stores/folders/store.ts index 88a5211e6bf..fcd27736e59 100644 --- a/apps/sim/stores/folders/store.ts +++ b/apps/sim/stores/folders/store.ts @@ -301,6 +301,3 @@ export const useIsWorkflowSelected = (workflowId: string) => export const useIsFolderSelected = (folderId: string) => useFolderStore((state) => state.selectedFolders.has(folderId)) - -export const useIsTaskSelected = (taskId: string) => - useFolderStore((state) => state.selectedTasks.has(taskId)) diff --git a/apps/sim/stores/folders/types.ts b/apps/sim/stores/folders/types.ts index 5982f6ed5eb..e028366617b 100644 --- a/apps/sim/stores/folders/types.ts +++ b/apps/sim/stores/folders/types.ts @@ -1,13 +1,3 @@ -export interface Workflow { - id: string - folderId?: string | null - name?: string - description?: string - userId?: string - workspaceId?: string - [key: string]: any // For additional properties -} - export interface WorkflowFolder { id: string name: string diff --git a/apps/sim/stores/notifications/index.ts b/apps/sim/stores/notifications/index.ts index 9e193094004..7c91679cf80 100644 --- a/apps/sim/stores/notifications/index.ts +++ b/apps/sim/stores/notifications/index.ts @@ -1,7 +1,3 @@ export { useNotificationStore } from './store' -export type { - AddNotificationParams, - Notification, - NotificationAction, -} from './types' +export type { Notification, NotificationAction } from './types' export { sendMothershipMessage } from './utils' diff --git a/apps/sim/stores/terminal/console/index.ts b/apps/sim/stores/terminal/console/index.ts index af22e59bb0c..cbebcdaf387 100644 --- a/apps/sim/stores/terminal/console/index.ts +++ b/apps/sim/stores/terminal/console/index.ts @@ -7,12 +7,4 @@ export { } from './storage' export { useConsoleEntry, useTerminalConsoleStore, useWorkflowConsoleEntries } from './store' export type { ConsoleEntry, ConsoleStore, ConsoleUpdate } from './types' -export { - normalizeConsoleError, - normalizeConsoleInput, - normalizeConsoleOutput, - safeConsoleStringify, - TERMINAL_CONSOLE_LIMITS, - trimConsoleEntries, - trimWorkflowConsoleEntries, -} from './utils' +export { safeConsoleStringify } from './utils' diff --git a/apps/sim/stores/terminal/index.ts b/apps/sim/stores/terminal/index.ts index 42854c0854c..5fdd3cc22a2 100644 --- a/apps/sim/stores/terminal/index.ts +++ b/apps/sim/stores/terminal/index.ts @@ -4,14 +4,8 @@ export { consolePersistence, type ExecutionPointer, loadExecutionPointer, - normalizeConsoleError, - normalizeConsoleInput, - normalizeConsoleOutput, safeConsoleStringify, saveExecutionPointer, - TERMINAL_CONSOLE_LIMITS, - trimConsoleEntries, - trimWorkflowConsoleEntries, useConsoleEntry, useTerminalConsoleStore, useWorkflowConsoleEntries, diff --git a/apps/sim/stores/variables/index.ts b/apps/sim/stores/variables/index.ts index d64ccb216a5..c468958196d 100644 --- a/apps/sim/stores/variables/index.ts +++ b/apps/sim/stores/variables/index.ts @@ -1,5 +1,4 @@ export { - getDefaultVariablesDimensions, getVariablesPosition, MAX_VARIABLES_HEIGHT, MAX_VARIABLES_WIDTH, diff --git a/apps/sim/stores/variables/modal.ts b/apps/sim/stores/variables/modal.ts index be8ff2915b7..157dcd75339 100644 --- a/apps/sim/stores/variables/modal.ts +++ b/apps/sim/stores/variables/modal.ts @@ -1,10 +1,6 @@ import { create } from 'zustand' import { devtools, persist } from 'zustand/middleware' -import type { - VariablesDimensions, - VariablesModalStore, - VariablesPosition, -} from '@/stores/variables/types' +import type { VariablesModalStore, VariablesPosition } from '@/stores/variables/types' /** * Floating variables modal default dimensions. @@ -135,11 +131,3 @@ export const useVariablesModalStore = create()( { name: 'variables-modal-store' } ) ) - -/** - * Get default floating variables modal dimensions. - */ -export const getDefaultVariablesDimensions = (): VariablesDimensions => ({ - width: DEFAULT_WIDTH, - height: DEFAULT_HEIGHT, -}) From ad100fa871b4d70550dbc3d1d5ddfcc51442fdbf Mon Sep 17 00:00:00 2001 From: Emir Karabeg <78010029+emir-karabeg@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:05:58 -0700 Subject: [PATCH 05/13] improvement(docs): ui/ux cleanup (#4016) * improvement(landing, blog): SEO and GEO optimization * improvement(docs): ui/ux cleanup * chore(blog): remove unused buildBlogJsonLd export and wordCount schema field * fix(blog): stack related posts vertically on mobile and fill all suggestion slots - Add flex-col sm:flex-row and matching border classes to related posts nav for consistent mobile stacking with the main blog page - Remove score > 0 filter in getRelatedPosts so it falls back to recent posts when there aren't enough tag matches - Align description text color with main page cards --- apps/docs/app/[lang]/[[...slug]]/page.tsx | 136 +-- apps/docs/app/[lang]/layout.tsx | 4 +- apps/docs/app/favicon.ico | Bin 0 -> 2191 bytes apps/docs/app/global.css | 880 +++++++++++++++--- apps/docs/app/layout.tsx | 17 +- apps/docs/app/robots.txt/route.ts | 1 - apps/docs/app/sitemap.xml/route.ts | 7 +- .../components/docs-layout/page-footer.tsx | 102 ++ .../docs-layout/page-navigation-arrows.tsx | 4 +- .../docs-layout/sidebar-components.tsx | 188 ++-- .../components/docs-layout/toc-footer.tsx | 39 - apps/docs/components/navbar/navbar.tsx | 31 +- apps/docs/components/page-actions.tsx | 2 +- apps/docs/components/structured-data.tsx | 43 +- apps/docs/components/ui/block-info-card.tsx | 27 +- apps/docs/components/ui/button.tsx | 2 +- apps/docs/components/ui/code-block.tsx | 1 + apps/docs/components/ui/dropdown-menu.tsx | 14 +- apps/docs/components/ui/faq.tsx | 93 +- apps/docs/components/ui/heading.tsx | 2 +- apps/docs/components/ui/image.tsx | 2 +- apps/docs/components/ui/language-dropdown.tsx | 132 +-- apps/docs/components/ui/lightbox.tsx | 2 +- apps/docs/components/ui/search-trigger.tsx | 3 +- apps/docs/components/ui/sim-logo.tsx | 60 +- apps/docs/components/ui/theme-toggle.tsx | 56 +- apps/docs/components/ui/video.tsx | 2 +- apps/docs/content/docs/de/meta.json | 4 + .../content/docs/en/api-reference/meta.json | 1 + .../content/docs/en/getting-started/index.mdx | 4 - .../content/docs/en/knowledgebase/meta.json | 2 +- apps/docs/content/docs/en/meta.json | 4 + apps/docs/content/docs/es/meta.json | 4 + apps/docs/content/docs/fr/meta.json | 4 + apps/docs/content/docs/ja/meta.json | 4 + apps/docs/content/docs/zh/meta.json | 4 + apps/docs/lib/cn.ts | 1 - apps/docs/next.config.ts | 4 +- apps/docs/public/favicon/favicon.ico | Bin 15086 -> 0 bytes apps/docs/public/favicon/favicon.svg | 3 - apps/docs/public/favicon/site.webmanifest | 2 +- apps/docs/public/icon.svg | 1 - apps/docs/source.config.ts | 6 +- .../app/(landing)/blog/[slug]/back-link.tsx | 41 +- .../sim/app/(landing)/blog/[slug]/loading.tsx | 65 +- apps/sim/app/(landing)/blog/[slug]/page.tsx | 167 ++-- .../app/(landing)/blog/authors/[id]/page.tsx | 60 +- apps/sim/app/(landing)/blog/layout.tsx | 13 +- apps/sim/app/(landing)/blog/page.tsx | 94 +- apps/sim/app/(landing)/blog/rss.xml/route.ts | 8 +- apps/sim/app/(landing)/blog/tags/page.tsx | 30 + .../navbar/components/github-stars.tsx | 2 +- apps/sim/app/sitemap.ts | 4 + apps/sim/lib/blog/faq.tsx | 14 +- apps/sim/lib/blog/registry.ts | 15 +- apps/sim/lib/blog/schema.ts | 1 + apps/sim/lib/blog/seo.ts | 45 +- apps/sim/public/llms.txt | 38 + biome.json | 1 + 59 files changed, 1660 insertions(+), 836 deletions(-) create mode 100644 apps/docs/app/favicon.ico create mode 100644 apps/docs/components/docs-layout/page-footer.tsx delete mode 100644 apps/docs/components/docs-layout/toc-footer.tsx delete mode 100644 apps/docs/lib/cn.ts delete mode 100644 apps/docs/public/favicon/favicon.ico delete mode 100644 apps/docs/public/favicon/favicon.svg delete mode 100644 apps/docs/public/icon.svg create mode 100644 apps/sim/public/llms.txt diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index 150326afb95..94b9531ec12 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx @@ -6,11 +6,9 @@ import { createAPIPage } from 'fumadocs-openapi/ui' import { Pre } from 'fumadocs-ui/components/codeblock' import defaultMdxComponents from 'fumadocs-ui/mdx' import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page' -import { ChevronLeft, ChevronRight } from 'lucide-react' -import Link from 'next/link' import { notFound } from 'next/navigation' +import { PageFooter } from '@/components/docs-layout/page-footer' import { PageNavigationArrows } from '@/components/docs-layout/page-navigation-arrows' -import { TOCFooter } from '@/components/docs-layout/toc-footer' import { LLMCopyButton } from '@/components/page-actions' import { StructuredData } from '@/components/structured-data' import { CodeBlock } from '@/components/ui/code-block' @@ -23,6 +21,15 @@ import { type PageData, source } from '@/lib/source' const SUPPORTED_LANGUAGES: Set = new Set(i18n.languages) const BASE_URL = 'https://docs.sim.ai' +const OG_LOCALE_MAP: Record = { + en: 'en_US', + es: 'es_ES', + fr: 'fr_FR', + de: 'de_DE', + ja: 'ja_JP', + zh: 'zh_CN', +} + function resolveLangAndSlug(params: { slug?: string[]; lang: string }) { const isValidLang = SUPPORTED_LANGUAGES.has(params.lang) const lang = isValidLang ? params.lang : 'en' @@ -120,101 +127,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l } const breadcrumbs = generateBreadcrumbs() - - const CustomFooter = () => ( -
-
- {neighbours?.previous ? ( - - - {neighbours.previous.name} - - ) : ( -
- )} - - {neighbours?.next ? ( - - {neighbours.next.name} - - - ) : ( -
- )} -
- -
- -
- -
- - -
- - -
- -
-
- ) + const footer = if (isOpenAPI && data.getAPIPageProps) { const apiProps = data.getAPIPageProps() @@ -233,7 +146,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l lang={lang} breadcrumb={breadcrumbs} /> - , + component: footer, }} >
@@ -259,7 +171,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
- {data.title} + {data.title} {data.description}
@@ -291,7 +203,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l tableOfContent={{ style: 'clerk', enabled: true, - footer: , single: false, }} tableOfContentPopover={{ @@ -300,7 +211,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l }} footer={{ enabled: true, - component: , + component: footer, }} >
@@ -310,7 +221,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l
- {data.title} + {data.title} {data.description}
@@ -393,10 +304,10 @@ export async function generateMetadata(props: { url: fullUrl, siteName: 'Sim Documentation', type: 'article', - locale: lang === 'en' ? 'en_US' : `${lang}_${lang.toUpperCase()}`, - alternateLocale: ['en', 'es', 'fr', 'de', 'ja', 'zh'] + locale: OG_LOCALE_MAP[lang] ?? 'en_US', + alternateLocale: i18n.languages .filter((l) => l !== lang) - .map((l) => (l === 'en' ? 'en_US' : `${l}_${l.toUpperCase()}`)), + .map((l) => OG_LOCALE_MAP[l] ?? 'en_US'), images: [ { url: ogImageUrl, @@ -416,17 +327,6 @@ export async function generateMetadata(props: { creator: '@simdotai', site: '@simdotai', }, - robots: { - index: true, - follow: true, - googleBot: { - index: true, - follow: true, - 'max-video-preview': -1, - 'max-image-preview': 'large', - 'max-snippet': -1, - }, - }, canonical: fullUrl, alternates: { canonical: fullUrl, diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx index e6a46813e74..5f9ca80cdd1 100644 --- a/apps/docs/app/[lang]/layout.tsx +++ b/apps/docs/app/[lang]/layout.tsx @@ -10,7 +10,6 @@ import { SidebarSeparator, } from '@/components/docs-layout/sidebar-components' import { Navbar } from '@/components/navbar/navbar' -import { AnimatedBlocks } from '@/components/ui/animated-blocks' import { SimLogoFull } from '@/components/ui/sim-logo' import { i18n } from '@/lib/i18n' import { source } from '@/lib/source' @@ -103,13 +102,12 @@ export default async function Layout({ children, params }: LayoutProps) {