diff --git a/apps/docs/content/docs/search/confluence.mdx b/apps/docs/content/docs/search/confluence.mdx
index 92ab84db517..79ea9ede7ba 100644
--- a/apps/docs/content/docs/search/confluence.mdx
+++ b/apps/docs/content/docs/search/confluence.mdx
@@ -58,7 +58,7 @@ Enter **Confluence Domain**, then choose one or more **Spaces**. The picker show
Open **More options** to change **Content Type**, **Filter by Label**, or **Metadata tags**. The default includes pages; choose **All content** for pages and blog posts. Leave the label filter empty unless you want a smaller scope.
-
+
@@ -37,7 +37,7 @@ In the new tab, select **Connect** and complete the provider's authorization. Ch
Return to Integrations when the connection completes, or select **Return to Search** to open your organization’s Search page. Your account is saved when authorization completes; there is no separate submit step. If the popup was blocked or closed, allow popups and select **Connect account** again. While authorization is pending, use **Open again**.
-
+
diff --git a/apps/docs/content/docs/search/github.mdx b/apps/docs/content/docs/search/github.mdx
index a1ab75a3178..91ad9551e78 100644
--- a/apps/docs/content/docs/search/github.mdx
+++ b/apps/docs/content/docs/search/github.mdx
@@ -45,7 +45,7 @@ https:///api/auth/oauth2/callback/github-repositories
Authorization starts from Sim so the callback can finish the pending connection. The connector polls GitHub's API and does not need a webhook.
-
+
*Example registration. Replace `sim.example.com` with your Sim domain.*
@@ -56,11 +56,11 @@ Authorization starts from Sim so the callback can finish the pending connection.
Expand **Permissions → Repository permissions**. Set **Contents → Access: Read-only**; leave the mandatory **Metadata** permission at **Read-only**.
-
+
Expand **Account permissions** and set **Email addresses → Access: Read-only**.
-
+
| Permission area | Permission | Access |
|---|---|---|
@@ -111,7 +111,7 @@ As a Sim organization admin, open **Settings → Sources → Add integration** a
Enter **Repository** as `owner/repo`. Open **More options** only if you need a different branch, path or extension filters, metadata tags, or a dedicated indexing account. **Sync documents with** defaults to **Connected members**.
-
+
| Field | What to enter |
|---|---|
diff --git a/apps/docs/content/docs/search/gitlab.mdx b/apps/docs/content/docs/search/gitlab.mdx
index 36915257d21..c60636bfc24 100644
--- a/apps/docs/content/docs/search/gitlab.mdx
+++ b/apps/docs/content/docs/search/gitlab.mdx
@@ -9,7 +9,7 @@ import { Image } from '@/components/ui/image'
GitLab Search uses an administrator connection to sync a project's content and permissions. Teammates do not connect individual GitLab accounts. They sign in to the Sim organization with a verified email matching their confirmed GitLab email.
-
+
Admin setup uses your organization's **Settings → Sources** page. Teammates do not need a personal GitLab connection. For workspace Search, use **Search → Add source** instead.
@@ -30,7 +30,7 @@ Use a self-managed GitLab instance running version **17.4 or later**. Setup need
Sign in to your self-managed GitLab instance as an instance administrator, then open **Avatar → Edit profile → Access → Personal access tokens**. On current releases choose **Generate token → Legacy token**; older versions show **Add new token** or the token form directly.
-
+
*Official [GitLab Handbook example](https://handbook.gitlab.com/handbook/security/product-security/security-platforms-architecture/product-security-engineering/runbooks/rotate-service-account-personal-access-tokens/). Navigation and button labels vary by version. The example's existing `api` tokens are unrelated to Sim; use the scopes below.*
@@ -63,7 +63,7 @@ Open **Settings → Sources → Add integration** and choose **GitLab**. On its
**More options** contains the repository and issue filters, **Max Items**, and **Metadata tags**. Unlike member-account sources, GitLab retains this optional item limit.
-
+
Select **Connect & Sync**. Sim validates the token and source policy, then starts indexing.
diff --git a/apps/docs/content/docs/search/gmail.mdx b/apps/docs/content/docs/search/gmail.mdx
index 40c9b5955b0..e207ab493df 100644
--- a/apps/docs/content/docs/search/gmail.mdx
+++ b/apps/docs/content/docs/search/gmail.mdx
@@ -39,7 +39,7 @@ Click **Add source**. Gmail appears in the provider's **Sources** list. Each per
-
+
## Connect your account
@@ -93,7 +93,7 @@ Users do not need to create Google Cloud credentials. The deployment operator co
https:///api/auth/oauth2/callback/google-email
```
-
+
This Google Cloud example uses one client for all three services. Replace `https://sim.example.com` with your Sim origin and add only the callbacks for services you enable.
diff --git a/apps/docs/content/docs/search/google-calendar.mdx b/apps/docs/content/docs/search/google-calendar.mdx
index 1ac04bc23b1..661e972445e 100644
--- a/apps/docs/content/docs/search/google-calendar.mdx
+++ b/apps/docs/content/docs/search/google-calendar.mdx
@@ -41,7 +41,7 @@ Keep the default date range for the previous and next 30 days. **More options**
-
+
`primary` means the connected person's main calendar. A calendar selected from the list is a specific calendar ID, even when it is your main calendar. That same ID applies to every member, and only members with access to it can search its events.
@@ -100,7 +100,7 @@ The deployment operator configures Google OAuth once; teammates then use the nor
https:///api/auth/oauth2/callback/google-calendar
```
-
+
This Google Cloud example uses one client for all three services. Replace `https://sim.example.com` with your Sim origin and add only the callbacks for services you enable.
diff --git a/apps/docs/content/docs/search/google-drive.mdx b/apps/docs/content/docs/search/google-drive.mdx
index 2e267fbc7f9..f7317aec464 100644
--- a/apps/docs/content/docs/search/google-drive.mdx
+++ b/apps/docs/content/docs/search/google-drive.mdx
@@ -56,7 +56,7 @@ Open **Settings → Sources → Google Drive → Sources → Add source**. This
This requires a Google Workspace domain and a Workspace super administrator to authorize domain-wide delegation. Consumer Gmail accounts cannot use this path.
-
+
@@ -65,11 +65,11 @@ This requires a Google Workspace domain and a Workspace super administrator to a
In [Google Cloud Console](https://console.cloud.google.com/), select your project and enable **Google Drive API** and **Admin SDK API** under **APIs & Services → Library**. Then open **IAM & Admin → Service Accounts → Create service account**, enter a name, and finish creation. Google Cloud project roles do not grant access to Workspace files; they are not required for this crawl.
-
+
Open the service account's **Keys** tab and choose **Add key → Create new key → JSON**, then select **Create** to download the key. Store it securely; you will add it to Sim next. See [Google's key creation guide](https://docs.cloud.google.com/iam/docs/keys-create-delete#creating).
-
+
@@ -78,7 +78,7 @@ Open the service account's **Keys** tab and choose **Add key → Create new key
In the service account's **Details**, expand **Advanced settings** and copy its numeric **Client ID**. Sign in to the [Workspace Admin Console](https://admin.google.com/ac/owl/domainwidedelegation) as a super administrator. Open **Security → Access and data control → API controls → Manage Domain Wide Delegation → Add new**.
-
+
Paste that Client ID into **Client ID**, then enter these exact scopes as a comma-separated list under **OAuth scopes**:
@@ -97,7 +97,7 @@ These are Search's central crawl scopes. The general [Google service account gui
Under **Indexing account**, choose the service-account connection action, or select an existing service account. Paste the JSON key into **Add Google Service Account**, give it a name, and add it. Sim returns you to the source form with that credential selected.
-
+
@@ -146,7 +146,7 @@ The deployment operator configures Google OAuth for member accounts and **Accoun
https:///api/auth/oauth2/callback/google-drive
```
-
+
This Google Cloud example uses one client for all three services. Replace `https://sim.example.com` with your Sim origin and add only the callbacks for services you enable.
diff --git a/apps/docs/content/docs/search/index.mdx b/apps/docs/content/docs/search/index.mdx
index 1b45c074528..907ea2876d3 100644
--- a/apps/docs/content/docs/search/index.mdx
+++ b/apps/docs/content/docs/search/index.mdx
@@ -39,7 +39,7 @@ Open **Integrations** in the main sidebar and select **Connect account** if prom
-
+
Source availability depends on the deployment and organization policy. An unavailable source needs operator configuration before setup can continue.
@@ -84,13 +84,13 @@ These requests are separate from organization invitations. They let recipients c
**Revoke all account access** withdraws every account that person contributed to the organization, including other providers. It does not remove their organization membership.
-
+
## Manage sources and documents
Open **Settings → Sources → [integration]**, then select a source. Providers with personal connections have **Sources** and **Accounts** tabs; providers such as GitLab open directly to the source list. Multiple sources can have different folder, repository, space, or project scopes.
-
+
| Source tab | What you can do |
| --- | --- |
@@ -100,9 +100,9 @@ Open **Settings → Sources → [integration]**, then select a source. Providers
Use the source header to sync, pause, resume, or remove that source. The back link returns to its integration. **Deactivate** on the integration page makes its content unavailable in Search, Assistant, and MCP while preserving its sources and connected accounts; **Activate** enables it again.
-
+
-
+
## Search, Assistant, and MCP
@@ -141,7 +141,7 @@ A completed sync means the source was checked; some documents may still be index
As an admin, open **Settings → Sources → [integration] → Sources → [source]**. In **Documents**, select **Failed** from the status dropdown to inspect those files. Use the search field to find a document by name. Select **Retry indexing** beside a file to try again. **Exclude** removes a file from search; select **Excluded** and then **Restore** to include it again. Fix a disconnected account or source configuration before retrying a sync that needs attention.
-
+
Temporary indexing-capacity waits retry automatically in the background. If automatic retries are exhausted, the document appears as failed so an admin can retry it. Other searchable documents remain available while this work finishes.
diff --git a/apps/docs/content/docs/search/jira.mdx b/apps/docs/content/docs/search/jira.mdx
index 21c421fe5c2..c15fa80f975 100644
--- a/apps/docs/content/docs/search/jira.mdx
+++ b/apps/docs/content/docs/search/jira.mdx
@@ -45,7 +45,7 @@ If you already know the project keys, use the switch beside **Projects** to sele
**Account for browsing** only populates the project picker. It does not enroll you or share that account's issue access with teammates.
-/api/auth/oauth2/callback/jira` to **Callback URLs**, keeping any callbacks already used by your deployment, then save.
-
+
## Before you start
@@ -34,7 +34,7 @@ Open **Settings → Sources → Add integration** and choose **Slack**. Select *
On the [Slack Apps page](https://api.slack.com/apps), create an app for the target workspace, or use an app dedicated to your Sim organization. Under **OAuth & Permissions**, add the user scopes and both redirect URLs listed below. Keep **Token Rotation** disabled.
-
+
*Official Slack example: [Basic Information](https://docs.slack.dev/tools/bolt-python/creating-an-app/#create-a-new-app). Use your own app's credentials.*
diff --git a/apps/sim/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage.test.ts b/apps/sim/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage.test.ts
index a17aaf1e831..2b749f2db55 100644
--- a/apps/sim/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage.test.ts
+++ b/apps/sim/app/(landing)/components/shared/responsive-design-stage/responsive-design-stage.test.ts
@@ -114,6 +114,43 @@ describe('calculateFitScale', () => {
})
describe('ResponsiveDesignStage', () => {
+ it.each([
+ { width: 280, height: 400, supportsZoom: true },
+ { width: 280, height: 400, supportsZoom: false },
+ { width: 350, height: 340, supportsZoom: true },
+ { width: 350, height: 340, supportsZoom: false },
+ ])(
+ 'fits an uncapped $width × $height stage with zoom support: $supportsZoom',
+ ({ width, height, supportsZoom }) => {
+ vi.stubGlobal('CSS', { supports: vi.fn(() => supportsZoom) })
+ act(() => {
+ root.render(
+ createElement(
+ ResponsiveDesignStage,
+ { width, height, maxScale: Number.POSITIVE_INFINITY },
+ createElement('span', null, 'Preview')
+ )
+ )
+ })
+
+ const surface = container.firstElementChild?.firstElementChild
+ if (!(surface instanceof HTMLElement) || !resizeObserver) {
+ throw new Error('responsive stage did not mount')
+ }
+ const observer = resizeObserver
+
+ act(() => observer.deliver(width * 2, height * 1.5))
+ expect(surface.style.zoom).toBe(supportsZoom ? '1.5' : '1')
+ expect(surface.style.transform).toBe(supportsZoom ? '' : 'scale(1.5)')
+ expect(surface.style.opacity).toBe('1')
+
+ act(() => observer.deliver(width / 2, height * 2))
+ expect(surface.style.zoom).toBe(supportsZoom ? '0.5' : '1')
+ expect(surface.style.transform).toBe(supportsZoom ? '' : 'scale(0.5)')
+ expect(surface.style.opacity).toBe('1')
+ }
+ )
+
it('hides an already visible surface until a measurable size returns', () => {
act(() => {
root.render(
diff --git a/apps/sim/app/(landing)/files/components/feature-graphics/file-library-graphic.module.css b/apps/sim/app/(landing)/files/components/feature-graphics/file-library-graphic.module.css
index 1fe3f88e310..eb11c05760a 100644
--- a/apps/sim/app/(landing)/files/components/feature-graphics/file-library-graphic.module.css
+++ b/apps/sim/app/(landing)/files/components/feature-graphics/file-library-graphic.module.css
@@ -44,9 +44,9 @@
.folderButton {
position: relative;
- min-width: 0;
- min-height: 0;
- container-type: size;
+ display: block;
+ width: 100%;
+ height: 100%;
cursor: pointer;
touch-action: manipulation;
border-radius: 12px;
@@ -60,11 +60,10 @@
width: 321px;
height: 270px;
left: 50%;
- bottom: 12px;
+ bottom: 0;
margin-left: -160.5px;
perspective: 800px;
transform-origin: bottom center;
- scale: min(tan(atan2(100cqw, 350px)), tan(atan2(100cqh, 340px)));
}
.folderBack {
position: absolute;
diff --git a/apps/sim/app/(landing)/files/components/feature-graphics/interactive-library-folder.tsx b/apps/sim/app/(landing)/files/components/feature-graphics/interactive-library-folder.tsx
index 0b423b59801..ad1d78fd5b5 100644
--- a/apps/sim/app/(landing)/files/components/feature-graphics/interactive-library-folder.tsx
+++ b/apps/sim/app/(landing)/files/components/feature-graphics/interactive-library-folder.tsx
@@ -3,6 +3,7 @@
import { useState } from 'react'
import { cn } from '@sim/emcn'
import { motion, useReducedMotion } from 'framer-motion'
+import { ResponsiveDesignStage } from '@/app/(landing)/components/shared/responsive-design-stage'
import styles from '@/app/(landing)/files/components/feature-graphics/file-library-graphic.module.css'
/**
@@ -72,53 +73,68 @@ export function InteractiveLibraryFolder({
: { type: 'spring' as const, stiffness: 120, damping: 14 }
return (
-
+
+
+
)
}
diff --git a/apps/sim/app/(landing)/workflows/components/feature-graphics/workflow-canvas-graphic.tsx b/apps/sim/app/(landing)/workflows/components/feature-graphics/workflow-canvas-graphic.tsx
index 8cb36e7b7b2..dcda47e3221 100644
--- a/apps/sim/app/(landing)/workflows/components/feature-graphics/workflow-canvas-graphic.tsx
+++ b/apps/sim/app/(landing)/workflows/components/feature-graphics/workflow-canvas-graphic.tsx
@@ -1,5 +1,6 @@
import { ChipTag, cn } from '@sim/emcn'
import colorMixFallbacks from '@/app/(landing)/components/shared/color-mix-fallbacks/color-mix-fallbacks.module.css'
+import { ResponsiveDesignStage } from '@/app/(landing)/components/shared/responsive-design-stage'
import { FeatureGraphicShell } from '@/app/(landing)/enterprise/components/feature-graphics'
import styles from '@/app/(landing)/workflows/components/feature-graphics/workflow-canvas-graphic.module.css'
@@ -65,11 +66,14 @@ const EDGE_DRAW_CLASSES = [styles.edgeDraw0, styles.edgeDraw1, styles.edgeDraw2]
export function WorkflowCanvasGraphic() {
return (
-