返回 oh-my-ppt
iconRegistry.ts
1 // Icon path data sourced from lucide-react v0.574.0 (ISC).
2 // We inline the SVG node tuples so inserted icons render without the React runtime.
3
4 export type IconNodeTuple =
5 | ['path', { d: string }]
6 | ['circle', { cx: number; cy: number; r: number }]
7 | ['ellipse', { cx: number; cy: number; rx: number; ry: number }]
8 | ['line', { x1: number; x2: number; y1: number; y2: number }]
9 | ['rect', { x: number; y: number; width: number; height: number; rx?: number; ry?: number }]
10
11 export type IconDefinition =
12 | {
13 id: string
14 label: string
15 variant?: 'stroke'
16 nodes: IconNodeTuple[]
17 }
18 | {
19 id: string
20 label: string
21 variant: 'badge'
22 badgeNumber: number
23 }
24
25 /** All registry icons share lucide's 24x24 viewBox. */
26 export const ICON_VIEWBOX = 24
27
28 const STROKE_ICONS: IconDefinition[] = [
29 {
30 id: 'sparkles',
31 label: 'Sparkles',
32 nodes: [
33 ['path', { d: 'M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z' }],
34 ['path', { d: 'M20 2v4' }],
35 ['path', { d: 'M22 4h-4' }],
36 ['circle', { cx: 4, cy: 20, r: 2 }]
37 ]
38 },
39 {
40 id: 'star',
41 label: 'Star',
42 nodes: [
43 ['path', { d: 'M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z' }]
44 ]
45 },
46 {
47 id: 'heart',
48 label: 'Heart',
49 nodes: [
50 ['path', { d: 'M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5' }]
51 ]
52 },
53 {
54 id: 'check',
55 label: 'Check',
56 nodes: [['path', { d: 'M20 6 9 17l-5-5' }]]
57 },
58 {
59 id: 'x',
60 label: 'Close',
61 nodes: [
62 ['path', { d: 'M18 6 6 18' }],
63 ['path', { d: 'm6 6 12 12' }]
64 ]
65 },
66 {
67 id: 'plus',
68 label: 'Plus',
69 nodes: [
70 ['path', { d: 'M5 12h14' }],
71 ['path', { d: 'M12 5v14' }]
72 ]
73 },
74 {
75 id: 'minus',
76 label: 'Minus',
77 nodes: [['path', { d: 'M5 12h14' }]]
78 },
79 {
80 id: 'arrow-left',
81 label: 'Arrow left',
82 nodes: [
83 ['path', { d: 'm12 19-7-7 7-7' }],
84 ['path', { d: 'M19 12H5' }]
85 ]
86 },
87 {
88 id: 'arrow-right',
89 label: 'Arrow right',
90 nodes: [
91 ['path', { d: 'M5 12h14' }],
92 ['path', { d: 'm12 5 7 7-7 7' }]
93 ]
94 },
95 {
96 id: 'arrow-up',
97 label: 'Arrow up',
98 nodes: [
99 ['path', { d: 'm5 12 7-7 7 7' }],
100 ['path', { d: 'M12 19V5' }]
101 ]
102 },
103 {
104 id: 'arrow-down',
105 label: 'Arrow down',
106 nodes: [
107 ['path', { d: 'M12 5v14' }],
108 ['path', { d: 'm19 12-7 7-7-7' }]
109 ]
110 },
111 {
112 id: 'arrow-up-right',
113 label: 'Arrow up right',
114 nodes: [
115 ['path', { d: 'M7 7h10v10' }],
116 ['path', { d: 'M7 17 17 7' }]
117 ]
118 },
119 {
120 id: 'chevron-left',
121 label: 'Chevron left',
122 nodes: [['path', { d: 'm15 18-6-6 6-6' }]]
123 },
124 {
125 id: 'chevron-right',
126 label: 'Chevron right',
127 nodes: [['path', { d: 'm9 18 6-6-6-6' }]]
128 },
129 {
130 id: 'chevron-up',
131 label: 'Chevron up',
132 nodes: [['path', { d: 'm18 15-6-6-6 6' }]]
133 },
134 {
135 id: 'chevron-down',
136 label: 'Chevron down',
137 nodes: [['path', { d: 'm6 9 6 6 6-6' }]]
138 },
139 {
140 id: 'circle-alert',
141 label: 'Alert',
142 nodes: [
143 ['circle', { cx: 12, cy: 12, r: 10 }],
144 ['line', { x1: 12, x2: 12, y1: 8, y2: 12 }],
145 ['line', { x1: 12, x2: 12.01, y1: 16, y2: 16 }]
146 ]
147 },
148 {
149 id: 'circle-help',
150 label: 'Help',
151 nodes: [
152 ['circle', { cx: 12, cy: 12, r: 10 }],
153 ['path', { d: 'M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3' }],
154 ['path', { d: 'M12 17h.01' }]
155 ]
156 },
157 {
158 id: 'info',
159 label: 'Info',
160 nodes: [
161 ['circle', { cx: 12, cy: 12, r: 10 }],
162 ['path', { d: 'M12 16v-4' }],
163 ['path', { d: 'M12 8h.01' }]
164 ]
165 },
166 {
167 id: 'lightbulb',
168 label: 'Lightbulb',
169 nodes: [
170 ['path', { d: 'M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5' }],
171 ['path', { d: 'M9 18h6' }],
172 ['path', { d: 'M10 22h4' }]
173 ]
174 },
175 {
176 id: 'target',
177 label: 'Target',
178 nodes: [
179 ['circle', { cx: 12, cy: 12, r: 10 }],
180 ['circle', { cx: 12, cy: 12, r: 6 }],
181 ['circle', { cx: 12, cy: 12, r: 2 }]
182 ]
183 },
184 {
185 id: 'clock',
186 label: 'Clock',
187 nodes: [
188 ['circle', { cx: 12, cy: 12, r: 10 }],
189 ['path', { d: 'M12 6v6l4 2' }]
190 ]
191 },
192 {
193 id: 'calendar',
194 label: 'Calendar',
195 nodes: [
196 ['path', { d: 'M8 2v4' }],
197 ['path', { d: 'M16 2v4' }],
198 ['rect', { x: 3, y: 4, width: 18, height: 18, rx: 2 }],
199 ['path', { d: 'M3 10h18' }]
200 ]
201 },
202 {
203 id: 'mail',
204 label: 'Mail',
205 nodes: [
206 ['rect', { x: 2, y: 4, width: 20, height: 16, rx: 2 }],
207 ['path', { d: 'm22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7' }]
208 ]
209 },
210 {
211 id: 'phone',
212 label: 'Phone',
213 nodes: [
214 ['path', { d: 'M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384' }]
215 ]
216 },
217 {
218 id: 'map-pin',
219 label: 'Map pin',
220 nodes: [
221 ['path', { d: 'M20 10c0 6-8 12-8 12S4 16 4 10a8 8 0 0 1 16 0' }],
222 ['circle', { cx: 12, cy: 10, r: 3 }]
223 ]
224 },
225 {
226 id: 'search',
227 label: 'Search',
228 nodes: [
229 ['path', { d: 'm21 21-4.34-4.34' }],
230 ['circle', { cx: 11, cy: 11, r: 8 }]
231 ]
232 },
233 {
234 id: 'link',
235 label: 'Link',
236 nodes: [
237 ['path', { d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71' }],
238 ['path', { d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71' }]
239 ]
240 },
241 {
242 id: 'download',
243 label: 'Download',
244 nodes: [
245 ['path', { d: 'M12 15V3' }],
246 ['path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }],
247 ['path', { d: 'm7 10 5 5 5-5' }]
248 ]
249 },
250 {
251 id: 'upload',
252 label: 'Upload',
253 nodes: [
254 ['path', { d: 'M12 3v12' }],
255 ['path', { d: 'm17 8-5-5-5 5' }],
256 ['path', { d: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4' }]
257 ]
258 },
259 {
260 id: 'chart-column',
261 label: 'Chart',
262 nodes: [
263 ['path', { d: 'M3 3v16a2 2 0 0 0 2 2h16' }],
264 ['path', { d: 'M18 17V9' }],
265 ['path', { d: 'M13 17V5' }],
266 ['path', { d: 'M8 17v-3' }]
267 ]
268 },
269 {
270 id: 'chart-pie',
271 label: 'Pie chart',
272 nodes: [
273 ['path', { d: 'M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951C12.449 1.996 12 2.449 12 3v8a1 1 0 0 0 1 1z' }],
274 ['path', { d: 'M21.21 15.89A10 10 0 1 1 8 2.83' }]
275 ]
276 },
277 {
278 id: 'trending-up',
279 label: 'Trending up',
280 nodes: [
281 ['path', { d: 'M16 7h6v6' }],
282 ['path', { d: 'm22 7-8.5 8.5-5-5L2 17' }]
283 ]
284 },
285 {
286 id: 'database',
287 label: 'Database',
288 nodes: [
289 ['ellipse', { cx: 12, cy: 5, rx: 9, ry: 3 }],
290 ['path', { d: 'M3 5v14a9 3 0 0 0 18 0V5' }],
291 ['path', { d: 'M3 12a9 3 0 0 0 18 0' }]
292 ]
293 },
294 {
295 id: 'image',
296 label: 'Image',
297 nodes: [
298 ['rect', { width: 18, height: 18, x: 3, y: 3, rx: 2, ry: 2 }],
299 ['circle', { cx: 9, cy: 9, r: 2 }],
300 ['path', { d: 'm21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21' }]
301 ]
302 },
303 {
304 id: 'video',
305 label: 'Video',
306 nodes: [
307 ['path', { d: 'm16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5' }],
308 ['rect', { x: 2, y: 6, width: 14, height: 12, rx: 2 }]
309 ]
310 },
311 {
312 id: 'file-text',
313 label: 'File',
314 nodes: [
315 ['path', { d: 'M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z' }],
316 ['path', { d: 'M14 2v5a1 1 0 0 0 1 1h5' }],
317 ['path', { d: 'M10 9H8' }],
318 ['path', { d: 'M16 13H8' }],
319 ['path', { d: 'M16 17H8' }]
320 ]
321 },
322 {
323 id: 'folder',
324 label: 'Folder',
325 nodes: [
326 ['path', { d: 'M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2z' }]
327 ]
328 },
329 {
330 id: 'book-open',
331 label: 'Book open',
332 nodes: [
333 ['path', { d: 'M12 7v14' }],
334 ['path', { d: 'M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z' }]
335 ]
336 },
337 {
338 id: 'users',
339 label: 'Users',
340 nodes: [
341 ['path', { d: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2' }],
342 ['path', { d: 'M16 3.128a4 4 0 0 1 0 7.744' }],
343 ['path', { d: 'M22 21v-2a4 4 0 0 0-3-3.87' }],
344 ['circle', { cx: 9, cy: 7, r: 4 }]
345 ]
346 },
347 {
348 id: 'user-check',
349 label: 'User check',
350 nodes: [
351 ['path', { d: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2' }],
352 ['circle', { cx: 9, cy: 7, r: 4 }],
353 ['path', { d: 'm16 11 2 2 4-4' }]
354 ]
355 },
356 {
357 id: 'shield-check',
358 label: 'Shield check',
359 nodes: [
360 ['path', { d: 'M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.68-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z' }],
361 ['path', { d: 'm9 12 2 2 4-4' }]
362 ]
363 },
364 {
365 id: 'lock',
366 label: 'Lock',
367 nodes: [
368 ['rect', { x: 3, y: 11, width: 18, height: 11, rx: 2 }],
369 ['path', { d: 'M7 11V7a5 5 0 0 1 10 0v4' }]
370 ]
371 },
372 {
373 id: 'globe',
374 label: 'Globe',
375 nodes: [
376 ['circle', { cx: 12, cy: 12, r: 10 }],
377 ['path', { d: 'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20' }],
378 ['path', { d: 'M2 12h20' }]
379 ]
380 },
381 {
382 id: 'flag',
383 label: 'Flag',
384 nodes: [
385 ['path', { d: 'M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c4 0 5 2 8 2 1.3 0 2.3-.3 3-.7a1 1 0 0 1 1 0V15a1 1 0 0 1-.4.8A6 6 0 0 1 16 17c-4 0-5-2-8-2a6 6 0 0 0-4 1.3' }]
386 ]
387 },
388 {
389 id: 'trophy',
390 label: 'Trophy',
391 nodes: [
392 ['path', { d: 'M10 14.66v1.626a2 2 0 0 1-.976 1.696A5 5 0 0 0 7 21h10a5 5 0 0 0-2.024-3.018A2 2 0 0 1 14 16.286V14.66' }],
393 ['path', { d: 'M18 9h1.5a1.5 1.5 0 0 0 0-3H18' }],
394 ['path', { d: 'M6 9H4.5a1.5 1.5 0 0 1 0-3H6' }],
395 ['path', { d: 'M6 2h12v7a6 6 0 0 1-12 0z' }]
396 ]
397 },
398 {
399 id: 'rocket',
400 label: 'Rocket',
401 nodes: [
402 ['path', { d: 'M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z' }],
403 ['path', { d: 'm12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z' }],
404 ['path', { d: 'M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0' }],
405 ['path', { d: 'M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5' }]
406 ]
407 },
408 {
409 id: 'zap',
410 label: 'Zap',
411 nodes: [['path', { d: 'M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46L12 9h8a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46L12 14z' }]]
412 },
413 {
414 id: 'settings',
415 label: 'Settings',
416 nodes: [
417 ['path', { d: 'M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915' }],
418 ['circle', { cx: 12, cy: 12, r: 3 }]
419 ]
420 }
421 ]
422
423 /** Numbered solid badges 1-9, common for step / process slides. */
424 const NUMBER_BADGES: IconDefinition[] = Array.from({ length: 9 }, (_, i) => ({
425 id: `number-${i + 1}`,
426 label: `Number ${i + 1}`,
427 variant: 'badge' as const,
428 badgeNumber: i + 1
429 }))
430
431 export const ICON_LIST: IconDefinition[] = [...STROKE_ICONS, ...NUMBER_BADGES]
432
433 const ICON_REGISTRY: Record<string, IconDefinition> = Object.fromEntries(
434 ICON_LIST.map((icon) => [icon.id, icon])
435 )
436
437 export function getIconDefinition(iconId: string): IconDefinition | undefined {
438 return ICON_REGISTRY[iconId]
439 }
440
441 export function isRegisteredIconId(iconId: string): boolean {
442 return Boolean(ICON_REGISTRY[iconId])
443 }
444
445 /** Outer <svg> attributes for the given icon variant. */
446 export function iconOuterSvgAttrs(def: IconDefinition): string {
447 if (def.variant === 'badge') {
448 // Badge: outer svg is just a coordinate frame; the inner circle uses currentColor fill.
449 return 'fill="none"'
450 }
451 return 'fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"'
452 }
453
454 /** Serialize an icon's inner SVG markup. Shared by the builder and the picker preview. */
455 export function serializeIconInner(def: IconDefinition): string {
456 if (def.variant === 'badge') {
457 const n = def.badgeNumber
458 return `<circle cx="12" cy="12" r="10" fill="currentColor" /><text x="12" y="17" text-anchor="middle" font-size="14" font-weight="700" fill="#fff" stroke="none" font-family="inherit">${n}</text>`
459 }
460 return def.nodes
461 .map(([tag, attrs]) => {
462 const parts = Object.entries(attrs).map(([key, value]) => `${key}="${value}"`)
463 return `<${tag} ${parts.join(' ')} />`
464 })
465 .join('')
466 }
467
467 lines TYPESCRIPT