From 651e38ab7bee253c204e6c9f6af0729926978e58 Mon Sep 17 00:00:00 2001 From: Waleed Date: Fri, 14 Aug 2026 14:40:37 -0700 Subject: [PATCH] Revert "fix(integrations): read every service mark from one registry (#6682)" This reverts commit b7a5f22abd98134a4332da16626c3695b892fced. --- .../connect-oauth-modal.tsx | 18 +- .../components/provider-icon/index.ts | 1 - .../provider-icon/provider-icon.tsx | 35 -- .../add-resource-dropdown.tsx | 1 + .../suggested-actions/suggested-actions.tsx | 40 +- .../[block]/integration-block-detail.tsx | 26 +- .../integrations-showcase.tsx | 31 +- .../connected-credential-detail.tsx | 19 +- .../integrations/integrations.tsx | 10 +- .../add-connector-modal.tsx | 24 +- .../connector-tile/connector-tile.tsx | 44 --- .../[id]/components/connector-tile/index.ts | 1 - .../connectors-section/connectors-section.tsx | 23 +- .../logs/components/log-details/utils.ts | 11 +- .../[tableId]/components/table-grid/types.ts | 6 +- .../tables/[tableId]/hooks/use-table.ts | 2 +- .../components/user-input/constants.ts | 262 +++++++++++++ .../components/user-input/hooks/index.ts | 5 + .../user-input/hooks/use-caret-viewport.ts | 77 ++++ .../user-input/hooks/use-mention-data.ts | 365 ++++++++++++++++++ .../hooks/use-mention-insert-handlers.ts | 137 +++++++ .../user-input/hooks/use-mention-keyboard.ts | 355 +++++++++++++++++ .../hooks/use-textarea-auto-resize.ts | 232 +++++++++++ .../copilot/components/user-input/types.ts | 11 + .../copilot/components/user-input/utils.ts | 79 ++++ .../credential-selector.tsx | 16 +- .../components/tag-dropdown/tag-dropdown.tsx | 13 +- .../components/tools/credential-selector.tsx | 16 +- .../panel/components/toolbar/toolbar.tsx | 3 +- .../[workflowId]/components/terminal/utils.ts | 5 +- .../preview-editor/preview-editor.tsx | 9 +- apps/sim/blocks/accent.ts | 7 - .../components/group-detail.tsx | 19 +- apps/sim/lib/integrations/index.ts | 1 - .../lib/integrations/oauth-service.test.ts | 94 ----- apps/sim/lib/integrations/oauth-service.ts | 66 ---- .../src/components/chip-modal/chip-modal.tsx | 16 +- 37 files changed, 1692 insertions(+), 388 deletions(-) delete mode 100644 apps/sim/app/workspace/[workspaceId]/components/provider-icon/index.ts delete mode 100644 apps/sim/app/workspace/[workspaceId]/components/provider-icon/provider-icon.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connector-tile/connector-tile.tsx delete mode 100644 apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connector-tile/index.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-caret-viewport.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-data.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-insert-handlers.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-mention-keyboard.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-textarea-auto-resize.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/types.ts diff --git a/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx b/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx index 96b2c2f8e3d..a5bd8cdf51a 100644 --- a/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/components/connect-oauth-modal/connect-oauth-modal.tsx @@ -19,7 +19,6 @@ import { useSession } from '@/lib/auth/auth-client' import type { OAuthReturnContext } from '@/lib/credentials/client-state' import { ADD_CONNECTOR_SEARCH_PARAM, writeOAuthReturnContext } from '@/lib/credentials/client-state' import { defaultCredentialDisplayName } from '@/lib/credentials/display-name' -import { resolveIntegrationBlockTypeForOAuth } from '@/lib/integrations' import { getProviderIdFromServiceId, OAUTH_PROVIDERS, @@ -27,7 +26,6 @@ import { parseProvider, } from '@/lib/oauth' import { getScopeDescription, getServiceConfigByProviderId } from '@/lib/oauth/utils' -import { BlockTile } from '@/blocks/block-tile' import { useCreateCredentialDraft, useWorkspaceCredentials } from '@/hooks/queries/credentials' import { useConnectOAuthService } from '@/hooks/queries/oauth/oauth-connections' @@ -175,20 +173,6 @@ export function ConnectOAuthModal(props: ConnectOAuthModalProps) { return resolveService(provider, props.serviceId ?? providerId) }, [props.serviceName, props.serviceIcon, props.provider, props.serviceId, providerId]) - /** - * The block behind this OAuth identity, so the dialog wears the same brand - * tile the canvas and the integrations catalog do. Falls back to the bare - * `OAUTH_PROVIDERS` mark for an id no catalog integration claims. - */ - const headerIcon = useMemo(() => { - const blockType = resolveIntegrationBlockTypeForOAuth( - props.serviceId, - props.provider, - providerId - ) - return blockType ? : ProviderIcon - }, [props.serviceId, props.provider, providerId, ProviderIcon]) - const workspaceId = isConnect ? props.workspaceId : '' const { data: credentials = [], isPending: credentialsLoading } = useWorkspaceCredentials({ workspaceId, @@ -359,7 +343,7 @@ export function ConnectOAuthModal(props: ConnectOAuthModalProps) { return ( - + {title} diff --git a/apps/sim/app/workspace/[workspaceId]/components/provider-icon/index.ts b/apps/sim/app/workspace/[workspaceId]/components/provider-icon/index.ts deleted file mode 100644 index f3206a41318..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/components/provider-icon/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ProviderIcon } from './provider-icon' diff --git a/apps/sim/app/workspace/[workspaceId]/components/provider-icon/provider-icon.tsx b/apps/sim/app/workspace/[workspaceId]/components/provider-icon/provider-icon.tsx deleted file mode 100644 index dd5f3bbdd55..00000000000 --- a/apps/sim/app/workspace/[workspaceId]/components/provider-icon/provider-icon.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client' - -import { cn } from '@sim/emcn' -import { SquareArrowUpRight } from '@sim/emcn/icons' -import { OAUTH_PROVIDERS, type OAuthProvider, parseProvider } from '@/lib/oauth' -import { getBareIconStyle, type StyleableIcon } from '@/blocks/brand-icon-style' - -interface ProviderIconProps { - provider: OAuthProvider - className?: string -} - -/** - * The mark for an OAuth provider, tinted with the brand colour its block - * config registers. Credential rows show a bare icon rather than the filled - * tile the canvas uses, so the colour has to come through `iconColor` — but it - * still comes from the same registry, which is what keeps a provider looking - * like itself everywhere it is listed. - * - * `OAUTH_PROVIDERS` carries the icon and no colour at all, so rendering - * straight from it is what left credential surfaces grey while the same - * service was branded a panel away. Falls back to a generic mark for a - * provider that map does not know. - */ -export function ProviderIcon({ provider, className }: ProviderIconProps) { - const { baseProvider } = parseProvider(provider) - const config = OAUTH_PROVIDERS[baseProvider] - - if (!config) return - - const Icon = config.icon as StyleableIcon - return ( - - ) -} diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx index 8dd8d3aad39..6f8653d1611 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/add-resource-dropdown/add-resource-dropdown.tsx @@ -258,6 +258,7 @@ export function useAvailableResources( id: integration.blockType, name: integration.name, iconComponent: integration.icon, + bgColor: integration.bgColor, })), }, { diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx index 49f0be5e844..8641b9b5eab 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/suggested-actions/suggested-actions.tsx @@ -7,6 +7,7 @@ import { randomFloat } from '@sim/utils/random' import { stripVersionSuffix } from '@sim/utils/string' import { useParams } from 'next/navigation' import { usePostHog } from 'posthog-js/react' +import { GmailIcon, SlackIcon } from '@/components/icons' import { INTEGRATIONS, type OAuthServiceMatch, @@ -15,7 +16,6 @@ import { } from '@/lib/integrations' import { captureEvent } from '@/lib/posthog/client' import { ConnectOAuthModal } from '@/app/workspace/[workspaceId]/components/connect-oauth-modal' -import { getBlockTileIcon } from '@/blocks/accent' import { getBareIconStyle } from '@/blocks/brand-icon-style' import { getAllBlockMeta } from '@/blocks/registry' import type { ModuleTag } from '@/blocks/types' @@ -224,16 +224,6 @@ function computeActions(services: readonly ServiceInfo[], signals: Signals): Act return [...integrations, ...prompts] } -/** - * Integrations pinned to the first paint. Named by block type so the mark comes - * from the same registry every other surface reads, rather than a second copy - * imported here that could drift from the block's own icon. - */ -const INITIAL_INTEGRATIONS = [ - { blockType: 'slack', slug: 'slack', name: 'Slack' }, - { blockType: 'gmail', slug: 'gmail', name: 'Gmail' }, -] as const - /** * Initial actions rendered on first paint, before OAuth/credentials queries * resolve. For users with no connections this is also the final result, so the @@ -241,20 +231,20 @@ const INITIAL_INTEGRATIONS = [ * before the personalized recompute replaces it. */ const INITIAL_ACTIONS: Action[] = [ - ...INITIAL_INTEGRATIONS.flatMap(({ blockType, slug, name }) => { - const icon = getBlockTileIcon(blockType) - return icon - ? [ - { - kind: 'integration', - id: `integrate-${slug}`, - label: `Integrate with ${name}`, - icon, - slug, - }, - ] - : [] - }), + { + kind: 'integration', + id: 'integrate-slack', + label: 'Integrate with Slack', + icon: SlackIcon, + slug: 'slack', + }, + { + kind: 'integration', + id: 'integrate-gmail', + label: 'Integrate with Gmail', + icon: GmailIcon, + slug: 'gmail', + }, toPromptAction(TABLE_STARTERS[0]), ...CANDIDATES.filter((c) => c.blockType === 'github' && c.featured) .slice(0, 1) diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx index 2a2cbf9dd23..f8fbafb5e47 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx @@ -8,12 +8,14 @@ import { useQueryState } from 'nuqs' import { HEADER_ACTION_CLUSTER, PAGE_HEADER_BAR } from '@/components/page-header-bar' import { isChatEnabled } from '@/lib/core/config/env-flags' import { + blockTypeToIconMap, type Integration, resolveCredentialDisplay, resolveOAuthServiceForIntegration, } from '@/lib/integrations' import { credentialProviderMatchesService } from '@/lib/oauth' import { ConnectOAuthModal } from '@/app/workspace/[workspaceId]/components/connect-oauth-modal' +import { RESOURCE_TILE_BASE } from '@/app/workspace/[workspaceId]/components/resource-tile' import { IntegrationSkillsSection } from '@/app/workspace/[workspaceId]/integrations/[block]/integration-skills-section' import { connectParam } from '@/app/workspace/[workspaceId]/integrations/[block]/search-params' import { @@ -32,7 +34,7 @@ import { SettingsResourceRow, } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row' import { SettingsSection } from '@/app/workspace/[workspaceId]/settings/components/settings-section/settings-section' -import { getBlockTileIcon } from '@/blocks/accent' +import { getTileIconColorClass } from '@/blocks/icon-color' import { storeCuratedPrompt } from '@/blocks/integration-matcher' import { getSuggestedSkillsForBlock, @@ -62,6 +64,7 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration useOAuthReturnRouter() const router = useRouter() const [connectMode, setConnectMode] = useQueryState(connectParam.key, connectParam.parser) + const Icon = blockTypeToIconMap[integration.type] const matchingTemplates = getTemplatesForBlock(integration.type) const suggestedSkills = getSuggestedSkillsForBlock(integration.type) const oauthService = resolveOAuthServiceForIntegration(integration) @@ -230,10 +233,16 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration >
- + {Icon ? ( + + ) : ( +
+ {integration.name.charAt(0)} +
+ )}

{integration.name}

{integration.description}

@@ -246,7 +255,7 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration } + icon={Icon && } title={credential.displayName} description={ credential.description || resolveCredentialDisplay(credential).subtitle @@ -365,7 +374,8 @@ function TemplateIcons({ blockTypes }: TemplateIconsProps) { return ( {blockTypes.map((bt, idx) => { - if (!getBlockTileIcon(bt)) return null + const ToolIcon = blockTypeToIconMap[bt] + if (!ToolIcon) return null const z = TEMPLATE_TILE_Z[idx] if (!z) return null const isTrailing = idx > 0 @@ -379,7 +389,7 @@ function TemplateIcons({ blockTypes }: TemplateIconsProps) { 'outline outline-2 outline-[var(--bg)] transition-[outline-color] duration-150 group-hover:outline-[var(--surface-active)]' )} > - + ) })} diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/components/integrations-showcase/integrations-showcase.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/components/integrations-showcase/integrations-showcase.tsx index e4b29e32451..43a25c08ba1 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/components/integrations-showcase/integrations-showcase.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/components/integrations-showcase/integrations-showcase.tsx @@ -5,7 +5,6 @@ import { RESOURCE_TILE_PLAIN, } from '@/app/workspace/[workspaceId]/components/resource-tile' import { getBlock } from '@/blocks' -import { getBlockTileIcon } from '@/blocks/accent' import { getTileIconColorClass } from '@/blocks/icon-color' /** @@ -60,15 +59,7 @@ function resolveBrandTileBg(blockType: string): string | null { interface IntegrationTileProps { blockType: string - /** - * Overrides the block's registered mark. Only for a tile whose identity is - * not the block itself — a credential issued by a family service account - * wears the family's corporate mark. Everything else takes the registry's, - * so the tile cannot end up with its fill and its icon from two sources. - */ - icon?: ComponentType<{ className?: string }> - /** Drawn when neither the override nor the registry supplies a mark. */ - fallbackLabel?: string + icon: ComponentType<{ className?: string }> framed?: boolean } @@ -77,23 +68,16 @@ interface IntegrationTileProps { * is a 36px tile used in list rows and headers; the framed variant adds an * outer 44px halo used inside the showcase grid. */ -export function IntegrationTile({ - blockType, - icon, - fallbackLabel, - framed = false, -}: IntegrationTileProps) { +export function IntegrationTile({ blockType, icon: Icon, framed = false }: IntegrationTileProps) { const brandBg = resolveBrandTileBg(blockType) - const Icon = icon ?? getBlockTileIcon(blockType) - const contentClass = getTileIconColorClass(brandBg) if (!framed) { return (
- {Icon ? : fallbackLabel} +
) } @@ -101,13 +85,10 @@ export function IntegrationTile({ return (
- {Icon ? : fallbackLabel} +
) diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/connected/[credentialId]/connected-credential-detail.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/connected/[credentialId]/connected-credential-detail.tsx index 705f6647dfd..2b1ae99f392 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/connected/[credentialId]/connected-credential-detail.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/connected/[credentialId]/connected-credential-detail.tsx @@ -8,6 +8,7 @@ import { ChipInput, ChipLink, ChipTextarea, + cn, Send, toast, } from '@sim/emcn' @@ -27,6 +28,10 @@ import { UnsavedChangesModal, useCredentialDetailForm, } from '@/app/workspace/[workspaceId]/components/credential-detail' +import { + RESOURCE_TILE_BASE, + RESOURCE_TILE_PLAIN, +} from '@/app/workspace/[workspaceId]/components/resource-tile' import { ConnectServiceAccountModal, type ServiceAccountProviderId, @@ -239,11 +244,15 @@ export function ConnectedCredentialDetail({ + display?.icon ? ( + + ) : ( +
+ + {resolveProviderLabel(credential.providerId).slice(0, 1) || '?'} + +
+ ) } title={headingTitle} subtitle={display?.detailSubtitle ?? 'Connected service'} diff --git a/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx b/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx index 3f26e43d390..9f166f59e8b 100644 --- a/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx +++ b/apps/sim/app/workspace/[workspaceId]/integrations/integrations.tsx @@ -14,6 +14,7 @@ import { import { useParams } from 'next/navigation' import { useQueryStates } from 'nuqs' import { + blockTypeToIconMap, formatIntegrationType, INTEGRATIONS, type Integration, @@ -33,7 +34,6 @@ import { } from '@/app/workspace/[workspaceId]/integrations/search-params' import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state' import { SettingsResourceRow } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row' -import { getBlockTileIcon } from '@/blocks/accent' import { useWorkspaceCredentials, type WorkspaceCredential } from '@/hooks/queries/credentials' import { useDebouncedSearchSetter } from '@/hooks/use-debounced-search-setter' import { usePermissionConfig } from '@/hooks/use-permission-config' @@ -68,6 +68,7 @@ interface IntegrationItemProps { workspaceId: string name: string description?: string | null + icon: ComponentType<{ className?: string }> unavailable?: boolean } @@ -77,12 +78,13 @@ function IntegrationItem({ workspaceId, name, description, + icon: Icon, unavailable = false, }: IntegrationItemProps) { return ( } + icon={} title={name} description={ unavailable @@ -346,7 +348,8 @@ export function Integrations() { {filteredCategorySections.map((section) => ( {section.integrations.map((integration) => { - if (!getBlockTileIcon(integration.type)) return null + const Icon = blockTypeToIconMap[integration.type] + if (!Icon) return null const availability = integrationAvailability.get(integration.type.toLowerCase()) const deploymentUnavailable = availability?.state === 'unavailable' || availability?.state === 'misconfigured' @@ -358,6 +361,7 @@ export function Integrations() { workspaceId={workspaceId} name={integration.name} description={integration.description} + icon={Icon} unavailable={integration.authType === 'oauth' && deploymentUnavailable} /> ) diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx index 38b6ca8ab26..a547065ff13 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/add-connector-modal/add-connector-modal.tsx @@ -16,6 +16,7 @@ import { ChipModalFooter, ChipModalHeader, type ComboboxOption, + cn, handleKeyboardActivation, Search, } from '@sim/emcn' @@ -30,11 +31,12 @@ import { import { ConnectOAuthModal } from '@/app/workspace/[workspaceId]/components/connect-oauth-modal' import { ConnectorConfigFields } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-config-fields' import { hasWorkspaceMaxConnectorAccess } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-entitlements' -import { ConnectorTile } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/connector-tile' import { SYNC_INTERVALS } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/consts' import { MaxBadge } from '@/app/workspace/[workspaceId]/knowledge/[id]/components/max-badge' import { useConnectorConfigFields } from '@/app/workspace/[workspaceId]/knowledge/[id]/hooks/use-connector-config-fields' import { useWorkspaceHostContext } from '@/app/workspace/[workspaceId]/providers/workspace-host-provider' +import { getBlock } from '@/blocks' +import { getTileIconColorClass } from '@/blocks/icon-color' import { CONNECTOR_META_REGISTRY } from '@/connectors/registry' import type { ConnectorMeta } from '@/connectors/types' import { useCreateConnector } from '@/hooks/queries/kb/connectors' @@ -477,6 +479,9 @@ interface ConnectorTypeCardProps { } function ConnectorTypeCard({ type, config, onClick }: ConnectorTypeCardProps) { + const Icon = config.icon + const brandBg = getBlock(type)?.bgColor ?? null + return (