Skip to content

Commit 7f5c33e

Browse files
authored
Merge pull request #11 from Panzer-Jack/dev
fix: figma 403 error
2 parents b6bc811 + 5e7fdb9 commit 7f5c33e

4 files changed

Lines changed: 171 additions & 38 deletions

File tree

src/services/figma/figma.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,53 @@ export class FigmaService {
185185
writeLogs("figma-simplified.yml", simplifiedResponse);
186186
return simplifiedResponse;
187187
}
188+
189+
async getImageData(
190+
fileKey: string,
191+
nodeId: string,
192+
): Promise<string> {
193+
const images = await this.request<GetImagesResponse>(
194+
`/images/${fileKey}?ids=${nodeId}&scale=2&format=png`,
195+
).then(({ images = {} }) => images)
196+
let image = ''
197+
198+
for (const key in images) {
199+
image = images[key] as string
200+
}
201+
202+
console.log(images, image)
203+
204+
return image
205+
}
206+
207+
async getSVG(
208+
fileKey: string,
209+
nodes: FetchImageParams[],
210+
localPath: string,
211+
): Promise<string[]> {
212+
213+
const svgIds = nodes.filter(({ fileType }) => fileType === 'svg').map(({ nodeId }) => nodeId)
214+
const svgFiles
215+
= svgIds.length > 0
216+
? this.request<GetImagesResponse>(
217+
`/images/${fileKey}?ids=${svgIds.join(',')}&format=svg`,
218+
).then(({ images = {} }) => images)
219+
: ({} as GetImagesResponse['images'])
220+
221+
const files = await Promise.all([svgFiles]).then(([l]) => ({ ...l }))
222+
223+
const downloads = nodes
224+
.map(({ nodeId, fileName }) => {
225+
const imageUrl = files[nodeId]
226+
if (imageUrl) {
227+
return downloadFigmaImage(fileName, localPath, imageUrl)
228+
}
229+
return false
230+
})
231+
.filter(url => !!url)
232+
233+
return Promise.all(downloads)
234+
}
188235
}
189236

190237
function writeLogs(name: string, value: any) {

src/services/figma/index.ts

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,34 +102,65 @@ export class FigmaTools {
102102
name: 'Download-Figma-Images',
103103
description: 'Download SVG and PNG images used in a Figma file based on the IDs of image or icon nodes',
104104
parameters: z.object({
105-
fileKey: z.string().describe('The key of the Figma file containing the node'),
105+
fileKey: z.string().describe("The key of the Figma file containing the node"),
106106
nodes: z
107107
.object({
108108
nodeId: z
109109
.string()
110-
.describe('The ID of the Figma image node to fetch, formatted as 1234:5678'),
110+
.describe("The ID of the Figma image node to fetch, formatted as 1234:5678"),
111111
imageRef: z
112112
.string()
113113
.optional()
114114
.describe(
115-
'If a node has an imageRef fill, you must include this variable. Leave blank when downloading Vector SVG images.',
115+
"If a node has an imageRef fill, you must include this variable. Leave blank when downloading Vector SVG images.",
116116
),
117-
fileName: z.string().describe('The local name for saving the fetched file'),
117+
fileName: z.string().describe("The local name for saving the fetched file"),
118118
})
119119
.array()
120-
.describe('The nodes to fetch as images'),
120+
.describe("The nodes to fetch as images"),
121+
pngScale: z
122+
.number()
123+
.positive()
124+
.optional()
125+
.default(2)
126+
.describe(
127+
"Export scale for PNG images. Optional, defaults to 2 if not specified. Affects PNG images only.",
128+
),
121129
localPath: z
122130
.string()
123131
.describe(
124-
'The absolute path to the directory where images are stored in the project. If the directory does not exist, it will be created. The format of this path should respect the directory format of the operating system you are running on. Don\'t use any special character escaping in the path name either.',
132+
"The absolute path to the directory where images are stored in the project. If the directory does not exist, it will be created. The format of this path should respect the directory format of the operating system you are running on. Don't use any special character escaping in the path name either.",
125133
),
134+
svgOptions: z
135+
.object({
136+
outlineText: z
137+
.boolean()
138+
.optional()
139+
.default(true)
140+
.describe("Whether to outline text in SVG exports. Default is true."),
141+
includeId: z
142+
.boolean()
143+
.optional()
144+
.default(false)
145+
.describe("Whether to include IDs in SVG exports. Default is false."),
146+
simplifyStroke: z
147+
.boolean()
148+
.optional()
149+
.default(true)
150+
.describe("Whether to simplify strokes in SVG exports. Default is true."),
151+
})
152+
.optional()
153+
.default({})
154+
.describe("Options for SVG export"),
126155
}),
127-
execute: async ({ fileKey, nodes, localPath }, { session }) => {
156+
execute: async ({ fileKey, nodes, localPath, svgOptions, pngScale }, { session }) => {
128157
try {
129158
const downloads = await this.figmaToolsCore.downloadFigmaImages({
130159
fileKey,
131160
nodes,
132161
localPath,
162+
svgOptions,
163+
pngScale,
133164
})
134165

135166
// If any download fails, return false
@@ -173,7 +204,11 @@ class FigmaToolsCore {
173204
}) {
174205
this.server = server
175206
this.figmaApiKey = figmaApiKey
176-
this.figmaService = new FigmaService(figmaApiKey)
207+
this.figmaService = new FigmaService({
208+
figmaApiKey: figmaApiKey,
209+
figmaOAuthToken: '',
210+
useOAuth: false,
211+
})
177212
}
178213

179214
public async figmaToCode({
@@ -203,7 +238,7 @@ class FigmaToolsCore {
203238
}
204239

205240
Logger.log('Generating YAML result from file')
206-
const yamlResult = yaml.dump(result)
241+
const yamlResult = JSON.stringify(result, null, 2)
207242

208243
Logger.log('Sending result to client')
209244
Logger.log('content', yamlResult)
@@ -214,30 +249,38 @@ class FigmaToolsCore {
214249
public async downloadFigmaImages({
215250
fileKey,
216251
nodes,
217-
localPath
252+
localPath,
253+
svgOptions,
254+
pngScale
218255
}: DownloadFigmaImagesParams) {
219256
const figmaService = this.figmaService
220257

221258
const imageFills = nodes.filter(({ imageRef }) => !!imageRef) as {
222-
nodeId: string
223-
imageRef: string
224-
fileName: string
225-
}[]
226-
const fillDownloads = figmaService.getImageFills(fileKey, imageFills, localPath)
259+
nodeId: string;
260+
imageRef: string;
261+
fileName: string;
262+
}[];
263+
const fillDownloads = figmaService.getImageFills(fileKey, imageFills, localPath);
227264
const renderRequests = nodes
228265
.filter(({ imageRef }) => !imageRef)
229266
.map(({ nodeId, fileName }) => ({
230267
nodeId,
231268
fileName,
232-
fileType: fileName.endsWith('.svg') ? ('svg' as const) : ('png' as const),
233-
}))
269+
fileType: fileName.endsWith(".svg") ? ("svg" as const) : ("png" as const),
270+
}));
234271

235-
const renderDownloads = figmaService.getImages(fileKey, renderRequests, localPath)
272+
const renderDownloads = figmaService.getImages(
273+
fileKey,
274+
renderRequests,
275+
localPath,
276+
pngScale,
277+
svgOptions,
278+
);
236279

237280
const downloads = await Promise.all([fillDownloads, renderDownloads]).then(([f, r]) => [
238281
...f,
239282
...r,
240-
])
283+
]);
241284

242285
return downloads
243286
}

src/services/utility/index.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,34 +40,65 @@ export class UtilityTools {
4040
name: 'download-svg-assets',
4141
description: '根据图像或图标节点的ID,仅下载Figma文件中使用的SVG资源',
4242
parameters: z.object({
43-
fileKey: z.string().describe('The key of the Figma file containing the node'),
43+
fileKey: z.string().describe("The key of the Figma file containing the node"),
4444
nodes: z
4545
.object({
4646
nodeId: z
4747
.string()
48-
.describe('The ID of the Figma image node to fetch, formatted as 1234:5678'),
48+
.describe("The ID of the Figma image node to fetch, formatted as 1234:5678"),
4949
imageRef: z
5050
.string()
5151
.optional()
5252
.describe(
53-
'If a node has an imageRef fill, you must include this variable. Leave blank when downloading Vector SVG images.',
53+
"If a node has an imageRef fill, you must include this variable. Leave blank when downloading Vector SVG images.",
5454
),
55-
fileName: z.string().describe('The local name for saving the fetched file'),
55+
fileName: z.string().describe("The local name for saving the fetched file"),
5656
})
5757
.array()
58-
.describe('The nodes to fetch as images'),
58+
.describe("The nodes to fetch as images"),
59+
pngScale: z
60+
.number()
61+
.positive()
62+
.optional()
63+
.default(2)
64+
.describe(
65+
"Export scale for PNG images. Optional, defaults to 2 if not specified. Affects PNG images only.",
66+
),
5967
localPath: z
6068
.string()
6169
.describe(
62-
'The absolute path to the directory where images are stored in the project. If the directory does not exist, it will be created. The format of this path should respect the directory format of the operating system you are running on. Don\'t use any special character escaping in the path name either.',
70+
"The absolute path to the directory where images are stored in the project. If the directory does not exist, it will be created. The format of this path should respect the directory format of the operating system you are running on. Don't use any special character escaping in the path name either.",
6371
),
72+
svgOptions: z
73+
.object({
74+
outlineText: z
75+
.boolean()
76+
.optional()
77+
.default(true)
78+
.describe("Whether to outline text in SVG exports. Default is true."),
79+
includeId: z
80+
.boolean()
81+
.optional()
82+
.default(false)
83+
.describe("Whether to include IDs in SVG exports. Default is false."),
84+
simplifyStroke: z
85+
.boolean()
86+
.optional()
87+
.default(true)
88+
.describe("Whether to simplify strokes in SVG exports. Default is true."),
89+
})
90+
.optional()
91+
.default({})
92+
.describe("Options for SVG export"),
6493
}),
65-
execute: async ({ fileKey, nodes, localPath }, { session }) => {
94+
execute: async ({ fileKey, nodes, localPath, pngScale, svgOptions }, { session }) => {
6695
try {
6796
const downloads = await this.figmaToolsCore.downloadFigmaSVGAssets({
6897
fileKey,
6998
nodes,
7099
localPath,
100+
pngScale,
101+
svgOptions,
71102
})
72103

73104
// If any download fails, return false

src/types/figma.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,31 @@
22
* Figma 图片下载参数类型定义
33
*/
44
export interface DownloadFigmaImagesParams {
5-
/** Figma 文件的唯一标识符,包含目标节点 */
6-
fileKey: string
7-
8-
/** 需要下载为图片的节点数组 */
9-
nodes: FigmaImageNode[]
5+
fileKey: string,
6+
nodes: FigmaImageNode[],
7+
localPath: string,
8+
pngScale: number,
9+
svgOptions: {
10+
outlineText: boolean;
11+
includeId: boolean;
12+
simplifyStroke: boolean;
13+
},
14+
}
1015

11-
/**
12-
* 项目中存储图片的绝对路径目录
13-
* 如果目录不存在,将会自动创建
14-
* 路径格式应遵循运行操作系统的目录格式,不要在路径名中使用任何特殊字符转义
16+
type FetchImageParams = {
17+
/**
18+
* The Node in Figma that will either be rendered or have its background image downloaded
1519
*/
16-
localPath: string
17-
}
20+
nodeId: string;
21+
/**
22+
* The local file name to save the image
23+
*/
24+
fileName: string;
25+
/**
26+
* The file mimetype for the image
27+
*/
28+
fileType: "png" | "svg";
29+
};
1830

1931
/**
2032
* Figma 图片节点类型定义
@@ -23,7 +35,7 @@ export interface FigmaImageNode {
2335
/**
2436
* Figma 图片节点的ID,格式为 1234:5678
2537
*/
26-
nodeId: string
38+
nodeId: string;
2739

2840
/**
2941
* 如果节点有 imageRef 填充,必须包含此变量

0 commit comments

Comments
 (0)