mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
Compare commits
1 Commits
main
...
feat/figma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3b35676a0 |
@@ -225,7 +225,6 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'activities:full': 'Full access to manage your Pipedrive activities',
|
||||
'mail:read': 'Read your Pipedrive emails',
|
||||
'mail:full': 'Full access to manage your Pipedrive emails',
|
||||
'projects:read': 'Read your Pipedrive projects',
|
||||
'projects:full': 'Full access to manage your Pipedrive projects',
|
||||
'webhooks:read': 'Read your Pipedrive webhooks',
|
||||
'webhooks:full': 'Full access to manage your Pipedrive webhooks',
|
||||
@@ -280,6 +279,13 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
|
||||
'user-follow-modify': 'Follow and unfollow artists and users',
|
||||
'user-read-playback-position': 'View your playback position in podcasts',
|
||||
'ugc-image-upload': 'Upload images to your Spotify playlists',
|
||||
// Figma scopes
|
||||
'current_user:read': 'Read your name, email, and profile image',
|
||||
'file_content:read': 'Read file contents and design data',
|
||||
'file_metadata:read': 'Read file metadata like name and last modified',
|
||||
'file_comments:read': 'Read comments on design files',
|
||||
'file_comments:write': 'Add and manage comments on design files',
|
||||
'library_content:read': 'Read published components and styles',
|
||||
}
|
||||
|
||||
function getScopeDescription(scope: string): string {
|
||||
|
||||
200
apps/sim/blocks/blocks/figma.ts
Normal file
200
apps/sim/blocks/blocks/figma.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import { FigmaIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { FigmaResponse } from '@/tools/figma/types'
|
||||
|
||||
export const FigmaBlock: BlockConfig<FigmaResponse> = {
|
||||
type: 'figma',
|
||||
name: 'Figma',
|
||||
description: 'Access Figma designs and assets',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrates Figma into the workflow. Get design files, export images, list and add comments, and access components and styles from your Figma workspace.',
|
||||
docsLink: 'https://docs.sim.ai/tools/figma',
|
||||
category: 'tools',
|
||||
bgColor: '#1E1E1E',
|
||||
icon: FigmaIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Get File', id: 'get_file' },
|
||||
{ label: 'Get Nodes', id: 'get_nodes' },
|
||||
{ label: 'Export Images', id: 'export_images' },
|
||||
{ label: 'List Comments', id: 'list_comments' },
|
||||
{ label: 'Add Comment', id: 'add_comment' },
|
||||
{ label: 'Get Components', id: 'get_components' },
|
||||
{ label: 'Get Styles', id: 'get_styles' },
|
||||
],
|
||||
value: () => 'get_file',
|
||||
},
|
||||
{
|
||||
id: 'credential',
|
||||
title: 'Figma Account',
|
||||
type: 'oauth-input',
|
||||
serviceId: 'figma',
|
||||
requiredScopes: [
|
||||
'current_user:read',
|
||||
'file_content:read',
|
||||
'file_metadata:read',
|
||||
'file_comments:read',
|
||||
'file_comments:write',
|
||||
'library_content:read',
|
||||
],
|
||||
placeholder: 'Select Figma account',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'fileKey',
|
||||
title: 'File Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'File key from URL (figma.com/file/{key}/...)',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'nodeIds',
|
||||
title: 'Node IDs',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated node IDs',
|
||||
condition: { field: 'operation', value: ['get_nodes', 'export_images'] },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'depth',
|
||||
title: 'Depth',
|
||||
type: 'short-input',
|
||||
placeholder: 'Document tree depth (optional)',
|
||||
condition: { field: 'operation', value: ['get_file', 'get_nodes'] },
|
||||
},
|
||||
{
|
||||
id: 'format',
|
||||
title: 'Format',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'PNG', id: 'png' },
|
||||
{ label: 'SVG', id: 'svg' },
|
||||
{ label: 'PDF', id: 'pdf' },
|
||||
{ label: 'JPG', id: 'jpg' },
|
||||
],
|
||||
value: () => 'png',
|
||||
condition: { field: 'operation', value: 'export_images' },
|
||||
},
|
||||
{
|
||||
id: 'scale',
|
||||
title: 'Scale',
|
||||
type: 'short-input',
|
||||
placeholder: 'Scale factor 0.01-4 (default: 1)',
|
||||
condition: { field: 'operation', value: 'export_images' },
|
||||
},
|
||||
{
|
||||
id: 'message',
|
||||
title: 'Comment Message',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter your comment message',
|
||||
condition: { field: 'operation', value: 'add_comment' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'nodeId',
|
||||
title: 'Node ID (Optional)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Attach comment to specific node',
|
||||
condition: { field: 'operation', value: 'add_comment' },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'figma_get_file',
|
||||
'figma_get_nodes',
|
||||
'figma_export_images',
|
||||
'figma_list_comments',
|
||||
'figma_add_comment',
|
||||
'figma_get_components',
|
||||
'figma_get_styles',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'get_file':
|
||||
return 'figma_get_file'
|
||||
case 'get_nodes':
|
||||
return 'figma_get_nodes'
|
||||
case 'export_images':
|
||||
return 'figma_export_images'
|
||||
case 'list_comments':
|
||||
return 'figma_list_comments'
|
||||
case 'add_comment':
|
||||
return 'figma_add_comment'
|
||||
case 'get_components':
|
||||
return 'figma_get_components'
|
||||
case 'get_styles':
|
||||
return 'figma_get_styles'
|
||||
default:
|
||||
throw new Error(`Invalid Figma operation: ${params.operation}`)
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { credential, operation, depth, scale, ...rest } = params
|
||||
|
||||
const baseParams: Record<string, unknown> = {
|
||||
credential,
|
||||
fileKey: rest.fileKey,
|
||||
}
|
||||
|
||||
if (depth && (operation === 'get_file' || operation === 'get_nodes')) {
|
||||
baseParams.depth = Number(depth)
|
||||
}
|
||||
|
||||
switch (operation) {
|
||||
case 'get_nodes':
|
||||
return {
|
||||
...baseParams,
|
||||
nodeIds: rest.nodeIds,
|
||||
}
|
||||
case 'export_images':
|
||||
return {
|
||||
...baseParams,
|
||||
nodeIds: rest.nodeIds,
|
||||
format: rest.format || 'png',
|
||||
scale: scale ? Number(scale) : undefined,
|
||||
}
|
||||
case 'add_comment':
|
||||
return {
|
||||
...baseParams,
|
||||
message: rest.message,
|
||||
nodeId: rest.nodeId || undefined,
|
||||
}
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
credential: { type: 'string', description: 'Figma OAuth credential' },
|
||||
fileKey: { type: 'string', description: 'Figma file key from URL' },
|
||||
nodeIds: { type: 'string', description: 'Comma-separated node IDs' },
|
||||
depth: { type: 'number', description: 'Document tree depth' },
|
||||
format: { type: 'string', description: 'Image export format' },
|
||||
scale: { type: 'number', description: 'Image export scale' },
|
||||
message: { type: 'string', description: 'Comment message' },
|
||||
nodeId: { type: 'string', description: 'Node ID to attach comment to' },
|
||||
},
|
||||
outputs: {
|
||||
name: { type: 'string', description: 'File name' },
|
||||
lastModified: { type: 'string', description: 'Last modified timestamp' },
|
||||
thumbnailUrl: { type: 'string', description: 'File thumbnail URL' },
|
||||
version: { type: 'string', description: 'File version' },
|
||||
document: { type: 'json', description: 'Document tree structure' },
|
||||
components: { type: 'json', description: 'Components in the file' },
|
||||
styles: { type: 'json', description: 'Styles in the file' },
|
||||
nodes: { type: 'json', description: 'Requested nodes' },
|
||||
files: { type: 'json', description: 'Exported image files' },
|
||||
comments: { type: 'json', description: 'Comments on the file' },
|
||||
comment: { type: 'json', description: 'Created comment' },
|
||||
metadata: { type: 'json', description: 'Operation metadata' },
|
||||
},
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
|
||||
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
|
||||
import { EvaluatorBlock } from '@/blocks/blocks/evaluator'
|
||||
import { ExaBlock } from '@/blocks/blocks/exa'
|
||||
import { FigmaBlock } from '@/blocks/blocks/figma'
|
||||
import { FileBlock } from '@/blocks/blocks/file'
|
||||
import { FirecrawlBlock } from '@/blocks/blocks/firecrawl'
|
||||
import { FunctionBlock } from '@/blocks/blocks/function'
|
||||
@@ -166,6 +167,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
evaluator: EvaluatorBlock,
|
||||
exa: ExaBlock,
|
||||
file: FileBlock,
|
||||
figma: FigmaBlock,
|
||||
firecrawl: FirecrawlBlock,
|
||||
function: FunctionBlock,
|
||||
generic_webhook: GenericWebhookBlock,
|
||||
|
||||
@@ -4223,3 +4223,41 @@ export function SpotifyIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function FigmaIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
shapeRendering='geometricPrecision'
|
||||
textRendering='geometricPrecision'
|
||||
imageRendering='optimizeQuality'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
viewBox='0 0 346 512.36'
|
||||
>
|
||||
<g fillRule='nonzero'>
|
||||
<path
|
||||
fill='#00B6FF'
|
||||
d='M172.53 246.9c0-42.04 34.09-76.11 76.12-76.11h11.01c.3.01.63-.01.94-.01 47.16 0 85.4 38.25 85.4 85.4 0 47.15-38.24 85.39-85.4 85.39-.31 0-.64-.01-.95-.01l-11 .01c-42.03 0-76.12-34.09-76.12-76.12V246.9z'
|
||||
/>
|
||||
<path
|
||||
fill='#24CB71'
|
||||
d='M0 426.98c0-47.16 38.24-85.41 85.4-85.41l87.13.01v84.52c0 47.65-39.06 86.26-86.71 86.26C38.67 512.36 0 474.13 0 426.98z'
|
||||
/>
|
||||
<path
|
||||
fill='#FF7237'
|
||||
d='M172.53.01v170.78h87.13c.3-.01.63.01.94.01 47.16 0 85.4-38.25 85.4-85.4C346 38.24 307.76 0 260.6 0c-.31 0-.64.01-.95.01h-87.12z'
|
||||
/>
|
||||
<path
|
||||
fill='#FF3737'
|
||||
d='M0 85.39c0 47.16 38.24 85.4 85.4 85.4h87.13V.01H85.39C38.24.01 0 38.24 0 85.39z'
|
||||
/>
|
||||
<path
|
||||
fill='#874FFF'
|
||||
d='M0 256.18c0 47.16 38.24 85.4 85.4 85.4h87.13V170.8H85.39C38.24 170.8 0 209.03 0 256.18z'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -229,6 +229,7 @@ export const auth = betterAuth({
|
||||
'hubspot',
|
||||
'linkedin',
|
||||
'spotify',
|
||||
'figma',
|
||||
|
||||
// Common SSO provider patterns
|
||||
...SSO_TRUSTED_PROVIDERS,
|
||||
@@ -1958,6 +1959,53 @@ export const auth = betterAuth({
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Figma provider
|
||||
{
|
||||
providerId: 'figma',
|
||||
clientId: env.FIGMA_CLIENT_ID as string,
|
||||
clientSecret: env.FIGMA_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://www.figma.com/oauth',
|
||||
tokenUrl: 'https://api.figma.com/v1/oauth/token',
|
||||
userInfoUrl: 'https://api.figma.com/v1/me',
|
||||
scopes: [
|
||||
'current_user:read',
|
||||
'file_content:read',
|
||||
'file_metadata:read',
|
||||
'file_comments:read',
|
||||
'file_comments:write',
|
||||
'library_content:read',
|
||||
],
|
||||
responseType: 'code',
|
||||
pkce: false,
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/figma`,
|
||||
getUserInfo: async (tokens) => {
|
||||
const response = await fetch('https://api.figma.com/v1/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokens.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
logger.error('Failed to fetch Figma user info', {
|
||||
status: response.status,
|
||||
})
|
||||
return null
|
||||
}
|
||||
|
||||
const profile = await response.json()
|
||||
|
||||
return {
|
||||
id: profile.id,
|
||||
name: profile.handle || profile.email || 'Figma User',
|
||||
email: profile.email,
|
||||
emailVerified: !!profile.email,
|
||||
image: profile.img_url,
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
// Include SSO plugin when enabled
|
||||
|
||||
@@ -233,6 +233,8 @@ export const env = createEnv({
|
||||
WORDPRESS_CLIENT_SECRET: z.string().optional(), // WordPress.com OAuth client secret
|
||||
SPOTIFY_CLIENT_ID: z.string().optional(), // Spotify OAuth client ID
|
||||
SPOTIFY_CLIENT_SECRET: z.string().optional(), // Spotify OAuth client secret
|
||||
FIGMA_CLIENT_ID: z.string().optional(), // Figma OAuth client ID
|
||||
FIGMA_CLIENT_SECRET: z.string().optional(), // Figma OAuth client secret
|
||||
|
||||
// E2B Remote Code Execution
|
||||
E2B_ENABLED: z.string().optional(), // Enable E2B remote code execution
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ConfluenceIcon,
|
||||
// DiscordIcon,
|
||||
DropboxIcon,
|
||||
FigmaIcon,
|
||||
GithubIcon,
|
||||
GmailIcon,
|
||||
GoogleCalendarIcon,
|
||||
@@ -72,6 +73,7 @@ export type OAuthProvider =
|
||||
| 'zoom'
|
||||
| 'wordpress'
|
||||
| 'spotify'
|
||||
| 'figma'
|
||||
| string
|
||||
|
||||
export type OAuthService =
|
||||
@@ -114,6 +116,7 @@ export type OAuthService =
|
||||
| 'zoom'
|
||||
| 'wordpress'
|
||||
| 'spotify'
|
||||
| 'figma'
|
||||
|
||||
export interface OAuthProviderConfig {
|
||||
id: OAuthProvider
|
||||
@@ -930,6 +933,30 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
|
||||
},
|
||||
defaultService: 'spotify',
|
||||
},
|
||||
figma: {
|
||||
id: 'figma',
|
||||
name: 'Figma',
|
||||
icon: (props) => FigmaIcon(props),
|
||||
services: {
|
||||
figma: {
|
||||
id: 'figma',
|
||||
name: 'Figma',
|
||||
description: 'Access Figma designs, components, styles, and assets.',
|
||||
providerId: 'figma',
|
||||
icon: (props) => FigmaIcon(props),
|
||||
baseProviderIcon: (props) => FigmaIcon(props),
|
||||
scopes: [
|
||||
'current_user:read',
|
||||
'file_content:read',
|
||||
'file_metadata:read',
|
||||
'file_comments:read',
|
||||
'file_comments:write',
|
||||
'library_content:read',
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultService: 'figma',
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1522,6 +1549,19 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
case 'figma': {
|
||||
const { clientId, clientSecret } = getCredentials(
|
||||
env.FIGMA_CLIENT_ID,
|
||||
env.FIGMA_CLIENT_SECRET
|
||||
)
|
||||
return {
|
||||
tokenEndpoint: 'https://api.figma.com/v1/oauth/token',
|
||||
clientId,
|
||||
clientSecret,
|
||||
useBasicAuth: false,
|
||||
supportsRefreshTokenRotation: false,
|
||||
}
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported provider: ${provider}`)
|
||||
}
|
||||
|
||||
80
apps/sim/tools/figma/add_comment.ts
Normal file
80
apps/sim/tools/figma/add_comment.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { FigmaAddCommentParams, FigmaAddCommentResponse } from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const figmaAddCommentTool: ToolConfig<FigmaAddCommentParams, FigmaAddCommentResponse> = {
|
||||
id: 'figma_add_comment',
|
||||
name: 'Figma - Add Comment',
|
||||
description: 'Add a comment to a Figma file, optionally on a specific node',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The comment message text',
|
||||
},
|
||||
nodeId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional node ID to attach the comment to a specific element',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.figma.com/v1/files/${params.fileKey}/comments`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
message: params.message,
|
||||
}
|
||||
|
||||
if (params.nodeId) {
|
||||
body.client_meta = {
|
||||
node_id: params.nodeId,
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
comment: data,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
comment: {
|
||||
type: 'json',
|
||||
description: 'The created comment object',
|
||||
},
|
||||
},
|
||||
}
|
||||
158
apps/sim/tools/figma/export_images.ts
Normal file
158
apps/sim/tools/figma/export_images.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import type {
|
||||
FigmaExportedFile,
|
||||
FigmaExportImagesParams,
|
||||
FigmaExportImagesResponse,
|
||||
} from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('FigmaExportImagesTool')
|
||||
|
||||
const FORMAT_MIME_TYPES: Record<string, string> = {
|
||||
png: 'image/png',
|
||||
svg: 'image/svg+xml',
|
||||
pdf: 'application/pdf',
|
||||
jpg: 'image/jpeg',
|
||||
}
|
||||
|
||||
export const figmaExportImagesTool: ToolConfig<FigmaExportImagesParams, FigmaExportImagesResponse> =
|
||||
{
|
||||
id: 'figma_export_images',
|
||||
name: 'Figma - Export Images',
|
||||
description: 'Export images from specific nodes in a Figma file as PNG, SVG, PDF, or JPG',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
nodeIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of node IDs to export as images',
|
||||
},
|
||||
format: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Image format: png, svg, pdf, or jpg',
|
||||
},
|
||||
scale: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Scale factor for the image (0.01 to 4, default: 1)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.figma.com/v1/images/${params.fileKey}`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
queryParams.append('ids', params.nodeIds)
|
||||
queryParams.append('format', params.format)
|
||||
|
||||
if (params.scale) {
|
||||
queryParams.append('scale', params.scale.toString())
|
||||
}
|
||||
|
||||
return `${baseUrl}?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
const data = await response.json()
|
||||
const images: Record<string, string> = data.images || {}
|
||||
const format = params?.format || 'png'
|
||||
const mimeType = FORMAT_MIME_TYPES[format] || 'image/png'
|
||||
|
||||
const files: FigmaExportedFile[] = []
|
||||
|
||||
for (const [nodeId, imageUrl] of Object.entries(images)) {
|
||||
if (!imageUrl) {
|
||||
logger.warn('No image URL for node', { nodeId })
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info('Downloading image', { nodeId, format })
|
||||
|
||||
const imageResponse = await fetch(imageUrl)
|
||||
|
||||
if (!imageResponse.ok) {
|
||||
logger.error('Failed to download image', {
|
||||
nodeId,
|
||||
status: imageResponse.status,
|
||||
statusText: imageResponse.statusText,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
const arrayBuffer = await imageResponse.arrayBuffer()
|
||||
const buffer = Buffer.from(arrayBuffer)
|
||||
|
||||
const sanitizedNodeId = nodeId.replace(/[^a-zA-Z0-9-_]/g, '_')
|
||||
const fileName = `figma_${sanitizedNodeId}.${format}`
|
||||
|
||||
files.push({
|
||||
name: fileName,
|
||||
mimeType,
|
||||
data: buffer,
|
||||
size: buffer.length,
|
||||
nodeId,
|
||||
})
|
||||
|
||||
logger.info('Image downloaded successfully', {
|
||||
nodeId,
|
||||
fileName,
|
||||
size: buffer.length,
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error downloading image', { nodeId, error })
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
files,
|
||||
metadata: {
|
||||
format,
|
||||
scale: params?.scale || 1,
|
||||
nodeCount: files.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
files: {
|
||||
type: 'file[]',
|
||||
description: 'Exported image files stored in execution files',
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Export metadata including format, scale, and node count',
|
||||
},
|
||||
},
|
||||
}
|
||||
66
apps/sim/tools/figma/get_components.ts
Normal file
66
apps/sim/tools/figma/get_components.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { FigmaGetComponentsParams, FigmaGetComponentsResponse } from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const figmaGetComponentsTool: ToolConfig<
|
||||
FigmaGetComponentsParams,
|
||||
FigmaGetComponentsResponse
|
||||
> = {
|
||||
id: 'figma_get_components',
|
||||
name: 'Figma - Get Components',
|
||||
description: 'Get all published components from a Figma file',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.figma.com/v1/files/${params.fileKey}/components`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const components = data.meta?.components || []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
components,
|
||||
metadata: {
|
||||
componentCount: components.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
components: {
|
||||
type: 'json',
|
||||
description: 'Array of components in the file',
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Metadata including component count',
|
||||
},
|
||||
},
|
||||
}
|
||||
101
apps/sim/tools/figma/get_file.ts
Normal file
101
apps/sim/tools/figma/get_file.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import type { FigmaGetFileParams, FigmaGetFileResponse } from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const figmaGetFileTool: ToolConfig<FigmaGetFileParams, FigmaGetFileResponse> = {
|
||||
id: 'figma_get_file',
|
||||
name: 'Figma - Get File',
|
||||
description:
|
||||
'Get the full document structure of a Figma file including all nodes, components, and styles',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
depth: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Depth of the document tree to return (optional)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.figma.com/v1/files/${params.fileKey}`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.depth) {
|
||||
queryParams.append('depth', params.depth.toString())
|
||||
}
|
||||
|
||||
const query = queryParams.toString()
|
||||
return query ? `${baseUrl}?${query}` : baseUrl
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
name: data.name,
|
||||
lastModified: data.lastModified,
|
||||
thumbnailUrl: data.thumbnailUrl,
|
||||
version: data.version,
|
||||
document: data.document,
|
||||
components: data.components || {},
|
||||
styles: data.styles || {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Name of the Figma file',
|
||||
},
|
||||
lastModified: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when the file was last modified',
|
||||
},
|
||||
thumbnailUrl: {
|
||||
type: 'string',
|
||||
description: 'URL to the file thumbnail',
|
||||
},
|
||||
version: {
|
||||
type: 'string',
|
||||
description: 'Current version of the file',
|
||||
},
|
||||
document: {
|
||||
type: 'json',
|
||||
description: 'Full document tree structure',
|
||||
},
|
||||
components: {
|
||||
type: 'json',
|
||||
description: 'Components defined in the file',
|
||||
},
|
||||
styles: {
|
||||
type: 'json',
|
||||
description: 'Styles defined in the file',
|
||||
},
|
||||
},
|
||||
}
|
||||
87
apps/sim/tools/figma/get_nodes.ts
Normal file
87
apps/sim/tools/figma/get_nodes.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import type { FigmaGetNodesParams, FigmaGetNodesResponse } from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const figmaGetNodesTool: ToolConfig<FigmaGetNodesParams, FigmaGetNodesResponse> = {
|
||||
id: 'figma_get_nodes',
|
||||
name: 'Figma - Get Nodes',
|
||||
description: 'Get specific nodes from a Figma file by their IDs',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
nodeIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of node IDs to retrieve',
|
||||
},
|
||||
depth: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Depth of the node subtree to return (optional)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = `https://api.figma.com/v1/files/${params.fileKey}/nodes`
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
queryParams.append('ids', params.nodeIds)
|
||||
|
||||
if (params.depth) {
|
||||
queryParams.append('depth', params.depth.toString())
|
||||
}
|
||||
|
||||
return `${baseUrl}?${queryParams.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
name: data.name,
|
||||
lastModified: data.lastModified,
|
||||
nodes: data.nodes || {},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Name of the Figma file',
|
||||
},
|
||||
lastModified: {
|
||||
type: 'string',
|
||||
description: 'Timestamp when the file was last modified',
|
||||
},
|
||||
nodes: {
|
||||
type: 'json',
|
||||
description: 'Map of node IDs to their document subtrees',
|
||||
},
|
||||
},
|
||||
}
|
||||
63
apps/sim/tools/figma/get_styles.ts
Normal file
63
apps/sim/tools/figma/get_styles.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type { FigmaGetStylesParams, FigmaGetStylesResponse } from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const figmaGetStylesTool: ToolConfig<FigmaGetStylesParams, FigmaGetStylesResponse> = {
|
||||
id: 'figma_get_styles',
|
||||
name: 'Figma - Get Styles',
|
||||
description: 'Get all published styles (colors, typography, effects, grids) from a Figma file',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.figma.com/v1/files/${params.fileKey}/styles`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const styles = data.meta?.styles || []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
styles,
|
||||
metadata: {
|
||||
styleCount: styles.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
styles: {
|
||||
type: 'json',
|
||||
description: 'Array of styles in the file (colors, typography, effects, grids)',
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Metadata including style count',
|
||||
},
|
||||
},
|
||||
}
|
||||
17
apps/sim/tools/figma/index.ts
Normal file
17
apps/sim/tools/figma/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { figmaAddCommentTool } from '@/tools/figma/add_comment'
|
||||
import { figmaExportImagesTool } from '@/tools/figma/export_images'
|
||||
import { figmaGetComponentsTool } from '@/tools/figma/get_components'
|
||||
import { figmaGetFileTool } from '@/tools/figma/get_file'
|
||||
import { figmaGetNodesTool } from '@/tools/figma/get_nodes'
|
||||
import { figmaGetStylesTool } from '@/tools/figma/get_styles'
|
||||
import { figmaListCommentsTool } from '@/tools/figma/list_comments'
|
||||
|
||||
export {
|
||||
figmaAddCommentTool,
|
||||
figmaExportImagesTool,
|
||||
figmaGetComponentsTool,
|
||||
figmaGetFileTool,
|
||||
figmaGetNodesTool,
|
||||
figmaGetStylesTool,
|
||||
figmaListCommentsTool,
|
||||
}
|
||||
64
apps/sim/tools/figma/list_comments.ts
Normal file
64
apps/sim/tools/figma/list_comments.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { FigmaListCommentsParams, FigmaListCommentsResponse } from '@/tools/figma/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const figmaListCommentsTool: ToolConfig<FigmaListCommentsParams, FigmaListCommentsResponse> =
|
||||
{
|
||||
id: 'figma_list_comments',
|
||||
name: 'Figma - List Comments',
|
||||
description: 'Get all comments on a Figma file',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'figma',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token',
|
||||
},
|
||||
fileKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The key of the Figma file (from the URL: figma.com/file/{fileKey}/...)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `https://api.figma.com/v1/files/${params.fileKey}/comments`,
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
const comments = data.comments || []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
comments,
|
||||
metadata: {
|
||||
commentCount: comments.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
comments: {
|
||||
type: 'json',
|
||||
description: 'Array of comments on the file',
|
||||
},
|
||||
metadata: {
|
||||
type: 'json',
|
||||
description: 'Metadata including comment count',
|
||||
},
|
||||
},
|
||||
}
|
||||
259
apps/sim/tools/figma/types.ts
Normal file
259
apps/sim/tools/figma/types.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* Base parameters for all Figma API requests
|
||||
*/
|
||||
export interface FigmaBaseParams {
|
||||
accessToken: string
|
||||
fileKey: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting a Figma file
|
||||
*/
|
||||
export interface FigmaGetFileParams extends FigmaBaseParams {
|
||||
depth?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for getting a Figma file
|
||||
*/
|
||||
export interface FigmaGetFileOutput {
|
||||
name: string
|
||||
lastModified: string
|
||||
thumbnailUrl: string
|
||||
version: string
|
||||
document: Record<string, unknown>
|
||||
components: Record<string, unknown>
|
||||
styles: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for getting a Figma file
|
||||
*/
|
||||
export interface FigmaGetFileResponse {
|
||||
success: boolean
|
||||
output: FigmaGetFileOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting specific nodes from a Figma file
|
||||
*/
|
||||
export interface FigmaGetNodesParams extends FigmaBaseParams {
|
||||
nodeIds: string
|
||||
depth?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for getting specific nodes
|
||||
*/
|
||||
export interface FigmaGetNodesOutput {
|
||||
name: string
|
||||
lastModified: string
|
||||
nodes: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for getting specific nodes
|
||||
*/
|
||||
export interface FigmaGetNodesResponse {
|
||||
success: boolean
|
||||
output: FigmaGetNodesOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for exporting images from a Figma file
|
||||
*/
|
||||
export interface FigmaExportImagesParams extends FigmaBaseParams {
|
||||
nodeIds: string
|
||||
format: 'png' | 'svg' | 'pdf' | 'jpg'
|
||||
scale?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Exported image file
|
||||
*/
|
||||
export interface FigmaExportedFile {
|
||||
name: string
|
||||
mimeType: string
|
||||
data: Buffer
|
||||
size: number
|
||||
nodeId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for exporting images
|
||||
*/
|
||||
export interface FigmaExportImagesOutput {
|
||||
files: FigmaExportedFile[]
|
||||
metadata: {
|
||||
format: string
|
||||
scale: number
|
||||
nodeCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for exporting images
|
||||
*/
|
||||
export interface FigmaExportImagesResponse {
|
||||
success: boolean
|
||||
output: FigmaExportImagesOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for listing comments on a Figma file
|
||||
*/
|
||||
export interface FigmaListCommentsParams extends FigmaBaseParams {}
|
||||
|
||||
/**
|
||||
* Comment structure from Figma API
|
||||
*/
|
||||
export interface FigmaComment {
|
||||
id: string
|
||||
message: string
|
||||
file_key: string
|
||||
parent_id?: string
|
||||
user: {
|
||||
id: string
|
||||
handle: string
|
||||
img_url: string
|
||||
}
|
||||
created_at: string
|
||||
resolved_at?: string
|
||||
order_id?: string
|
||||
client_meta?: {
|
||||
node_id?: string
|
||||
node_offset?: { x: number; y: number }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for listing comments
|
||||
*/
|
||||
export interface FigmaListCommentsOutput {
|
||||
comments: FigmaComment[]
|
||||
metadata: {
|
||||
commentCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for listing comments
|
||||
*/
|
||||
export interface FigmaListCommentsResponse {
|
||||
success: boolean
|
||||
output: FigmaListCommentsOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for adding a comment to a Figma file
|
||||
*/
|
||||
export interface FigmaAddCommentParams extends FigmaBaseParams {
|
||||
message: string
|
||||
nodeId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for adding a comment
|
||||
*/
|
||||
export interface FigmaAddCommentOutput {
|
||||
comment: FigmaComment
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for adding a comment
|
||||
*/
|
||||
export interface FigmaAddCommentResponse {
|
||||
success: boolean
|
||||
output: FigmaAddCommentOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting components from a Figma file
|
||||
*/
|
||||
export interface FigmaGetComponentsParams extends FigmaBaseParams {}
|
||||
|
||||
/**
|
||||
* Component structure from Figma API
|
||||
*/
|
||||
export interface FigmaComponent {
|
||||
key: string
|
||||
name: string
|
||||
description: string
|
||||
node_id: string
|
||||
thumbnail_url: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
containing_frame?: {
|
||||
name: string
|
||||
nodeId: string
|
||||
pageName: string
|
||||
pageId: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for getting components
|
||||
*/
|
||||
export interface FigmaGetComponentsOutput {
|
||||
components: FigmaComponent[]
|
||||
metadata: {
|
||||
componentCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for getting components
|
||||
*/
|
||||
export interface FigmaGetComponentsResponse {
|
||||
success: boolean
|
||||
output: FigmaGetComponentsOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for getting styles from a Figma file
|
||||
*/
|
||||
export interface FigmaGetStylesParams extends FigmaBaseParams {}
|
||||
|
||||
/**
|
||||
* Style structure from Figma API
|
||||
*/
|
||||
export interface FigmaStyle {
|
||||
key: string
|
||||
name: string
|
||||
description: string
|
||||
style_type: 'FILL' | 'TEXT' | 'EFFECT' | 'GRID'
|
||||
node_id: string
|
||||
thumbnail_url: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Output for getting styles
|
||||
*/
|
||||
export interface FigmaGetStylesOutput {
|
||||
styles: FigmaStyle[]
|
||||
metadata: {
|
||||
styleCount: number
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for getting styles
|
||||
*/
|
||||
export interface FigmaGetStylesResponse {
|
||||
success: boolean
|
||||
output: FigmaGetStylesOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for all Figma responses
|
||||
*/
|
||||
export type FigmaResponse =
|
||||
| FigmaGetFileResponse
|
||||
| FigmaGetNodesResponse
|
||||
| FigmaExportImagesResponse
|
||||
| FigmaListCommentsResponse
|
||||
| FigmaAddCommentResponse
|
||||
| FigmaGetComponentsResponse
|
||||
| FigmaGetStylesResponse
|
||||
@@ -185,6 +185,15 @@ import {
|
||||
exaResearchTool,
|
||||
exaSearchTool,
|
||||
} from '@/tools/exa'
|
||||
import {
|
||||
figmaAddCommentTool,
|
||||
figmaExportImagesTool,
|
||||
figmaGetComponentsTool,
|
||||
figmaGetFileTool,
|
||||
figmaGetNodesTool,
|
||||
figmaGetStylesTool,
|
||||
figmaListCommentsTool,
|
||||
} from '@/tools/figma'
|
||||
import { fileParseTool } from '@/tools/file'
|
||||
import {
|
||||
firecrawlCrawlTool,
|
||||
@@ -1379,6 +1388,13 @@ export const tools: Record<string, ToolConfig> = {
|
||||
firecrawl_crawl: firecrawlCrawlTool,
|
||||
firecrawl_map: firecrawlMapTool,
|
||||
firecrawl_extract: firecrawlExtractTool,
|
||||
figma_get_file: figmaGetFileTool,
|
||||
figma_get_nodes: figmaGetNodesTool,
|
||||
figma_export_images: figmaExportImagesTool,
|
||||
figma_list_comments: figmaListCommentsTool,
|
||||
figma_add_comment: figmaAddCommentTool,
|
||||
figma_get_components: figmaGetComponentsTool,
|
||||
figma_get_styles: figmaGetStylesTool,
|
||||
grafana_get_dashboard: grafanaGetDashboardTool,
|
||||
grafana_list_dashboards: grafanaListDashboardsTool,
|
||||
grafana_create_dashboard: grafanaCreateDashboardTool,
|
||||
|
||||
Reference in New Issue
Block a user