Aktraiser commited on
Commit
0eed2ad
·
1 Parent(s): 7b4a53b

background shape for figjam

Browse files
Files changed (1) hide show
  1. app.py +206 -227
app.py CHANGED
@@ -251,127 +251,6 @@ def get_figma_comments(file_id: str = "") -> str:
251
 
252
  return f"📝 **Commentaires récents :**\n" + "\n\n".join(comment_list)
253
 
254
- def get_figma_collaborators(file_id: str = "") -> str:
255
- """Liste qui travaille sur un fichier Figma (collaborateurs actifs)"""
256
- file_id = file_id or figma_config["file_id"]
257
-
258
- if not file_id:
259
- return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
260
-
261
- collaborators = {}
262
-
263
- # 1. Récupérer les commentateurs récents
264
- comments_result = make_figma_request(f"files/{file_id}/comments")
265
- if "error" not in comments_result:
266
- comments = comments_result.get("comments", [])
267
- for comment in comments[-20:]: # 20 commentaires les plus récents
268
- user_data = comment.get("user", {})
269
- if user_data:
270
- user_id = user_data.get("id")
271
- if user_id:
272
- collaborators[user_id] = {
273
- "nom": user_data.get("handle", "Anonyme"),
274
- "avatar": user_data.get("img_url", ""),
275
- "derniere_activite": comment.get("created_at", ""),
276
- "type_activite": "💬 Commentaire",
277
- "dernier_commentaire": comment.get("message", "")[:100] + "..." if len(comment.get("message", "")) > 100 else comment.get("message", "")
278
- }
279
-
280
- # 2. Récupérer les versions récentes pour voir les contributeurs
281
- versions_result = make_figma_request(f"files/{file_id}/versions")
282
- if "error" not in versions_result:
283
- versions = versions_result.get("versions", [])
284
- for version in versions[-10:]: # 10 versions les plus récentes
285
- user_data = version.get("user", {})
286
- if user_data:
287
- user_id = user_data.get("id")
288
- if user_id:
289
- if user_id not in collaborators:
290
- collaborators[user_id] = {
291
- "nom": user_data.get("handle", "Anonyme"),
292
- "avatar": user_data.get("img_url", ""),
293
- "derniere_activite": version.get("created_at", ""),
294
- "type_activite": "🎨 Modification",
295
- "description": version.get("description", "Modification du design")
296
- }
297
- else:
298
- # Mettre à jour avec l'activité la plus récente
299
- version_date = version.get("created_at", "")
300
- current_date = collaborators[user_id]["derniere_activite"]
301
- if version_date > current_date:
302
- collaborators[user_id]["derniere_activite"] = version_date
303
- collaborators[user_id]["type_activite"] = "🎨 Modification"
304
- collaborators[user_id]["description"] = version.get("description", "Modification du design")
305
-
306
- if not collaborators:
307
- return "👥 Aucune activité récente trouvée sur ce fichier"
308
-
309
- # Trier par dernière activité
310
- sorted_collaborators = sorted(
311
- collaborators.items(),
312
- key=lambda x: x[1]["derniere_activite"],
313
- reverse=True
314
- )
315
-
316
- collaborator_list = []
317
- for user_id, data in sorted_collaborators:
318
- # Formater la date
319
- date_str = data["derniere_activite"]
320
- if date_str:
321
- try:
322
- from datetime import datetime
323
- dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
324
- date_formatted = dt.strftime("%d/%m/%Y %H:%M")
325
- except:
326
- date_formatted = date_str
327
- else:
328
- date_formatted = "Date inconnue"
329
-
330
- collaborator_info = f"👤 **{data['nom']}**\n {data['type_activite']} - {date_formatted}"
331
-
332
- # Ajouter des détails selon le type d'activité
333
- if "dernier_commentaire" in data:
334
- collaborator_info += f"\n 💬 \"{data['dernier_commentaire']}\""
335
- elif "description" in data:
336
- collaborator_info += f"\n 📝 {data['description']}"
337
-
338
- collaborator_list.append(collaborator_info)
339
-
340
- return f"👥 **Collaborateurs actifs sur ce fichier :**\n\n" + "\n\n".join(collaborator_list)
341
-
342
- def list_figma_recent_files() -> str:
343
- """Liste tous les fichiers Figma récents de l'utilisateur connecté"""
344
- result = make_figma_request("files/recent")
345
-
346
- if "error" in result:
347
- return f"❌ Erreur : {result['error']}"
348
-
349
- files = result.get("files", [])
350
-
351
- if not files:
352
- return "📁 Aucun fichier récent trouvé"
353
-
354
- file_list = []
355
- for file_data in files[:20]: # Limiter à 20 fichiers récents
356
- name = file_data.get("name", "Sans nom")
357
- file_key = file_data.get("key", "")
358
- last_modified = file_data.get("last_modified", "")
359
-
360
- # Extraire juste la date de la timestamp
361
- if last_modified:
362
- try:
363
- from datetime import datetime
364
- dt = datetime.fromisoformat(last_modified.replace('Z', '+00:00'))
365
- date_str = dt.strftime("%d/%m/%Y %H:%M")
366
- except:
367
- date_str = last_modified
368
- else:
369
- date_str = "Date inconnue"
370
-
371
- file_list.append(f"📄 **{name}**\n ID: `{file_key}`\n Modifié: {date_str}")
372
-
373
- return f"📁 **Vos fichiers Figma récents :**\n\n" + "\n\n".join(file_list)
374
-
375
  def get_figma_user_info() -> str:
376
  """Récupère les informations de l'utilisateur connecté"""
377
  result = make_figma_request("me")
@@ -412,27 +291,16 @@ def list_figma_team_projects(team_id: str = "") -> str:
412
 
413
  return f"📁 **Projets de l'équipe :**\n" + "\n".join(project_list)
414
 
415
- def create_figma_sticky_note(x: str, y: str, text: str, color: str = "yellow", name: str = "Sticky Note") -> str:
416
- """Crée un post-it dans FigJam"""
417
  if not figma_config["file_id"]:
418
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
419
 
420
  try:
421
  x_pos, y_pos = float(x), float(y)
 
422
 
423
- # Couleurs disponibles pour les post-its
424
- colors = {
425
- "yellow": "🟡",
426
- "blue": "🔵",
427
- "green": "🟢",
428
- "red": "🔴",
429
- "purple": "🟣",
430
- "orange": "🟠"
431
- }
432
-
433
- color_emoji = colors.get(color.lower(), "🟡")
434
-
435
- comment_text = f"{color_emoji} **Post-it à créer :**\n- Nom: {name}\n- Position: ({x_pos}, {y_pos})\n- Texte: \"{text}\"\n- Couleur: {color}"
436
 
437
  comment_data = {
438
  "message": comment_text,
@@ -447,13 +315,35 @@ def create_figma_sticky_note(x: str, y: str, text: str, color: str = "yellow", n
447
  if "error" in result:
448
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
449
 
450
- return f"✅ Post-it {color_emoji} \"{text}\" créé à ({x_pos}, {y_pos})"
451
 
452
  except ValueError:
453
- return "❌ Les coordonnées doivent être des nombres"
454
 
455
- def create_figma_section(x: str, y: str, width: str, height: str, title: str, color: str = "blue") -> str:
456
- """Crée une section dans FigJam pour organiser le contenu"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  if not figma_config["file_id"]:
458
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
459
 
@@ -461,7 +351,19 @@ def create_figma_section(x: str, y: str, width: str, height: str, title: str, co
461
  x_pos, y_pos = float(x), float(y)
462
  w, h = float(width), float(height)
463
 
464
- comment_text = f"📦 **Section à créer :**\n- Titre: {title}\n- Position: ({x_pos}, {y_pos})\n- Taille: {w}x{h}\n- Couleur: {color}"
 
 
 
 
 
 
 
 
 
 
 
 
465
 
466
  comment_data = {
467
  "message": comment_text,
@@ -476,27 +378,27 @@ def create_figma_section(x: str, y: str, width: str, height: str, title: str, co
476
  if "error" in result:
477
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
478
 
479
- return f"✅ Section \"{title}\" créée à ({x_pos}, {y_pos}) avec la taille {w}x{h}"
480
 
481
  except ValueError:
482
  return "❌ Les coordonnées et dimensions doivent être des nombres"
483
 
484
- def create_figma_connector(start_x: str, start_y: str, end_x: str, end_y: str, style: str = "solid") -> str:
485
- """Crée un connecteur entre deux points dans FigJam"""
486
  if not figma_config["file_id"]:
487
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
488
 
489
  try:
490
- sx, sy = float(start_x), float(start_y)
491
- ex, ey = float(end_x), float(end_y)
492
 
493
- comment_text = f"🔗 **Connecteur à créer :**\n- De: ({sx}, {sy})\n- Vers: ({ex}, {ey})\n- Style: {style}"
494
 
495
  comment_data = {
496
  "message": comment_text,
497
  "client_meta": {
498
- "x": sx,
499
- "y": sy
500
  }
501
  }
502
 
@@ -505,32 +407,23 @@ def create_figma_connector(start_x: str, start_y: str, end_x: str, end_y: str, s
505
  if "error" in result:
506
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
507
 
508
- return f"✅ Connecteur créé de ({sx}, {sy}) vers ({ex}, {ey})"
509
 
510
  except ValueError:
511
- return "❌ Toutes les coordonnées doivent être des nombres"
512
 
513
- def create_figma_shape(x: str, y: str, width: str, height: str, shape_type: str, name: str = "Shape", color: str = "#FF0000") -> str:
514
- """Crée une forme géométrique (circle, triangle, star, etc.)"""
515
  if not figma_config["file_id"]:
516
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
517
 
518
  try:
519
  x_pos, y_pos = float(x), float(y)
520
- w, h = float(width), float(height)
521
-
522
- shapes = {
523
- "circle": "⭕",
524
- "triangle": "🔺",
525
- "star": "⭐",
526
- "diamond": "💎",
527
- "heart": "❤️",
528
- "arrow": "➡️"
529
- }
530
 
531
- shape_emoji = shapes.get(shape_type.lower(), "🔶")
 
532
 
533
- comment_text = f"{shape_emoji} **Forme à créer :**\n- Nom: {name}\n- Type: {shape_type}\n- Position: ({x_pos}, {y_pos})\n- Taille: {w}x{h}\n- Couleur: {color}"
534
 
535
  comment_data = {
536
  "message": comment_text,
@@ -545,20 +438,22 @@ def create_figma_shape(x: str, y: str, width: str, height: str, shape_type: str,
545
  if "error" in result:
546
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
547
 
548
- return f"✅ Forme {shape_emoji} {shape_type} créée à ({x_pos}, {y_pos})"
549
 
550
  except ValueError:
551
- return "❌ Les coordonnées et dimensions doivent être des nombres"
552
 
553
- def move_figma_element(element_name: str, new_x: str, new_y: str) -> str:
554
- """Déplace un élément existant vers une nouvelle position"""
555
  if not figma_config["file_id"]:
556
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
557
 
558
  try:
559
- x_pos, y_pos = float(new_x), float(new_y)
 
 
560
 
561
- comment_text = f"🚀 **Déplacement à effectuer :**\n- Élément: {element_name}\n- Nouvelle position: ({x_pos}, {y_pos})"
562
 
563
  comment_data = {
564
  "message": comment_text,
@@ -573,26 +468,51 @@ def move_figma_element(element_name: str, new_x: str, new_y: str) -> str:
573
  if "error" in result:
574
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
575
 
576
- return f"✅ Instruction de déplacement créée pour {element_name} vers ({x_pos}, {y_pos})"
577
 
578
  except ValueError:
579
- return "❌ Les coordonnées doivent être des nombres"
580
 
581
- def resize_figma_element(element_name: str, new_width: str, new_height: str) -> str:
582
- """Redimensionne un élément existant"""
583
  if not figma_config["file_id"]:
584
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
585
 
586
  try:
587
- w, h = float(new_width), float(new_height)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
 
589
- comment_text = f"📏 **Redimensionnement à effectuer :**\n- Élément: {element_name}\n- Nouvelle taille: {w}x{h}"
 
 
590
 
591
  comment_data = {
592
  "message": comment_text,
593
  "client_meta": {
594
- "x": 0,
595
- "y": 0
596
  }
597
  }
598
 
@@ -601,26 +521,83 @@ def resize_figma_element(element_name: str, new_width: str, new_height: str) ->
601
  if "error" in result:
602
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
603
 
604
- return f"✅ Instruction de redimensionnement créée pour {element_name} {w}x{h}"
605
 
606
  except ValueError:
607
- return "❌ Les dimensions doivent être des nombres"
608
 
609
- def create_figma_layout_grid(columns: str, rows: str, gap: str = "20") -> str:
610
- """Crée une grille de layout pour organiser les éléments"""
611
  if not figma_config["file_id"]:
612
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
613
 
614
  try:
615
- cols, rows_num, gap_size = int(columns), int(rows), float(gap)
616
 
617
- comment_text = f"🔲 **Grille de layout à créer :**\n- Colonnes: {cols}\n- Lignes: {rows_num}\n- Espacement: {gap_size}px"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
618
 
619
  comment_data = {
620
  "message": comment_text,
621
  "client_meta": {
622
- "x": 0,
623
- "y": 0
624
  }
625
  }
626
 
@@ -629,21 +606,29 @@ def create_figma_layout_grid(columns: str, rows: str, gap: str = "20") -> str:
629
  if "error" in result:
630
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
631
 
632
- return f"✅ Grille {cols}x{rows_num} créée avec espacement de {gap_size}px"
633
 
634
  except ValueError:
635
- return "❌ Les valeurs doivent être des nombres"
636
 
637
- def create_figma_component(x: str, y: str, width: str, height: str, component_name: str, description: str = "") -> str:
638
- """Crée un composant réutilisable"""
639
  if not figma_config["file_id"]:
640
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
641
 
642
  try:
643
  x_pos, y_pos = float(x), float(y)
644
  w, h = float(width), float(height)
 
 
 
 
 
 
 
 
645
 
646
- comment_text = f"🧩 **Composant à créer :**\n- Nom: {component_name}\n- Position: ({x_pos}, {y_pos})\n- Taille: {w}x{h}\n- Description: {description}"
647
 
648
  comment_data = {
649
  "message": comment_text,
@@ -658,7 +643,7 @@ def create_figma_component(x: str, y: str, width: str, height: str, component_na
658
  if "error" in result:
659
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
660
 
661
- return f"✅ Composant \"{component_name}\" créé à ({x_pos}, {y_pos})"
662
 
663
  except ValueError:
664
  return "❌ Les coordonnées et dimensions doivent être des nombres"
@@ -678,12 +663,6 @@ def setup_demo():
678
  def test_user():
679
  return get_figma_user_info()
680
 
681
- def test_recent_files():
682
- return list_figma_recent_files()
683
-
684
- def test_collaborators():
685
- return get_figma_collaborators()
686
-
687
  with gr.Blocks(
688
  title="🎨 Figma MCP Server",
689
  theme=gr.themes.Soft(),
@@ -738,11 +717,9 @@ def setup_demo():
738
 
739
  # Actions de test
740
  with gr.Row():
741
- test_files_btn = gr.Button("📁 Fichiers Récents")
742
  test_info_btn = gr.Button("📄 Info Fichier")
743
  test_comments_btn = gr.Button("📝 Commentaires")
744
  test_user_btn = gr.Button("👤 Info Utilisateur")
745
- test_collaborators_btn = gr.Button("👥 Collaborateurs")
746
 
747
  # Connexions des événements
748
  token_btn.click(
@@ -757,11 +734,6 @@ def setup_demo():
757
  outputs=[status_output]
758
  )
759
 
760
- test_files_btn.click(
761
- test_recent_files,
762
- outputs=[status_output]
763
- )
764
-
765
  test_info_btn.click(
766
  test_file_info,
767
  outputs=[status_output]
@@ -776,11 +748,6 @@ def setup_demo():
776
  test_user,
777
  outputs=[status_output]
778
  )
779
-
780
- test_collaborators_btn.click(
781
- test_collaborators,
782
- outputs=[status_output]
783
- )
784
 
785
  gr.Markdown("""
786
  ---
@@ -791,30 +758,42 @@ def setup_demo():
791
  - `configure_figma_file_id(file_id)` - Configure l'ID du fichier
792
 
793
  **📁 Navigation :**
794
- - `list_figma_recent_files()` - Liste tous vos fichiers récents avec leurs IDs
795
  - `get_figma_file_info()` - Récupère les infos du fichier
796
  - `get_figma_comments()` - Récupère les commentaires
797
  - `get_figma_user_info()` - Info utilisateur connecté
798
- - `get_figma_collaborators()` - Récupère les collaborateurs actifs
799
 
800
- **🎨 Création d'éléments de base :**
801
  - `create_figma_rectangle(x, y, width, height, name, color)` - Crée un rectangle
802
  - `create_figma_frame(x, y, width, height, name)` - Crée un frame
803
  - `create_figma_text(x, y, text, name, font_size)` - Crée un texte
804
- - `create_figma_shape(x, y, width, height, shape_type, name, color)` - Crée des formes (circle, triangle, star, diamond, heart, arrow)
805
-
806
- **🟡 FigJam - Post-its et organisation :**
807
- - `create_figma_sticky_note(x, y, text, color, name)` - Crée un post-it (couleurs: yellow, blue, green, red, purple, orange)
808
- - `create_figma_section(x, y, width, height, title, color)` - Crée une section pour organiser
809
- - `create_figma_connector(start_x, start_y, end_x, end_y, style)` - Crée un connecteur entre points
810
-
811
- **🔧 Manipulation d'objets :**
812
- - `move_figma_element(element_name, new_x, new_y)` - Déplace un élément
813
- - `resize_figma_element(element_name, new_width, new_height)` - Redimensionne un élément
814
 
815
- **🏗️ Layout et organisation :**
816
- - `create_figma_layout_grid(columns, rows, gap)` - Crée une grille de layout
817
- - `create_figma_component(x, y, width, height, component_name, description)` - Crée un composant réutilisable
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
  """)
819
 
820
  return demo
 
251
 
252
  return f"📝 **Commentaires récents :**\n" + "\n\n".join(comment_list)
253
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  def get_figma_user_info() -> str:
255
  """Récupère les informations de l'utilisateur connecté"""
256
  result = make_figma_request("me")
 
291
 
292
  return f"📁 **Projets de l'équipe :**\n" + "\n".join(project_list)
293
 
294
+ def create_figjam_sticky_note(x: str, y: str, text: str, width: str = "240", height: str = "240") -> str:
295
+ """Crée un post-it (sticky note) dans FigJam avec les dimensions officielles"""
296
  if not figma_config["file_id"]:
297
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
298
 
299
  try:
300
  x_pos, y_pos = float(x), float(y)
301
+ w, h = float(width), float(height)
302
 
303
+ comment_text = f"🟡 **Sticky Note à créer (API FigJam) :**\n- Position: ({x_pos}, {y_pos})\n- Taille: {w}x{h}px (défaut: 240x240)\n- Texte: \"{text}\"\n\n📋 **Code Plugin Figma :**\n```javascript\nconst sticky = figma.createSticky()\nsticky.x = {x_pos}\nsticky.y = {y_pos}\nsticky.resize({w}, {h})\nawait figma.loadFontAsync(sticky.text.fontName)\nsticky.text.characters = '{text}'\n```"
 
 
 
 
 
 
 
 
 
 
 
 
304
 
305
  comment_data = {
306
  "message": comment_text,
 
315
  if "error" in result:
316
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
317
 
318
+ return f"✅ Instructions Sticky Note créées à ({x_pos}, {y_pos}) - Taille: {w}x{h}px"
319
 
320
  except ValueError:
321
+ return "❌ Les coordonnées et dimensions doivent être des nombres"
322
 
323
+ def create_figjam_connector_between_elements(element1_name: str, element2_name: str, style: str = "solid") -> str:
324
+ """Crée un connecteur entre deux éléments FigJam (officiel)"""
325
+ if not figma_config["file_id"]:
326
+ return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
327
+
328
+ comment_text = f"🔗 **Connecteur FigJam à créer (API officielle) :**\n- Entre: {element1_name}\n- Et: {element2_name}\n- Style: {style}\n\n📋 **Code Plugin Figma :**\n```javascript\n// Trouver les éléments par nom\nconst element1 = figma.currentPage.findOne(n => n.name === '{element1_name}')\nconst element2 = figma.currentPage.findOne(n => n.name === '{element2_name}')\n\n// Créer le connecteur\nconst connector = figma.createConnector()\nconnector.connectorStart = {{\n endpointNodeId: element1.id,\n magnet: 'AUTO'\n}}\nconnector.connectorEnd = {{\n endpointNodeId: element2.id,\n magnet: 'AUTO'\n}}\n```"
329
+
330
+ comment_data = {
331
+ "message": comment_text,
332
+ "client_meta": {
333
+ "x": 0,
334
+ "y": 0
335
+ }
336
+ }
337
+
338
+ result = make_figma_request(f"files/{figma_config['file_id']}/comments", "POST", comment_data)
339
+
340
+ if "error" in result:
341
+ return f"❌ Erreur lors de la création du commentaire : {result['error']}"
342
+
343
+ return f"✅ Instructions connecteur créées entre {element1_name} et {element2_name}"
344
+
345
+ def create_figjam_shape_with_text(x: str, y: str, shape_type: str, text: str, width: str = "208", height: str = "208") -> str:
346
+ """Crée une forme avec texte intégré dans FigJam (API officielle)"""
347
  if not figma_config["file_id"]:
348
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
349
 
 
351
  x_pos, y_pos = float(x), float(y)
352
  w, h = float(width), float(height)
353
 
354
+ # Types de formes supportés par l'API Figma
355
+ supported_shapes = {
356
+ "rectangle": "ROUNDED_RECTANGLE",
357
+ "circle": "ELLIPSE",
358
+ "triangle": "TRIANGLE",
359
+ "diamond": "DIAMOND",
360
+ "star": "STAR",
361
+ "hexagon": "HEXAGON"
362
+ }
363
+
364
+ figma_shape = supported_shapes.get(shape_type.lower(), "ROUNDED_RECTANGLE")
365
+
366
+ comment_text = f"🔶 **Forme avec texte FigJam (API officielle) :**\n- Position: ({x_pos}, {y_pos})\n- Type: {shape_type} → {figma_shape}\n- Taille: {w}x{h}px (défaut: 208x208)\n- Texte: \"{text}\"\n\n📋 **Code Plugin Figma :**\n```javascript\nconst shape = figma.createShapeWithText()\nshape.x = {x_pos}\nshape.y = {y_pos}\nshape.resize({w}, {h})\nshape.shapeType = '{figma_shape}'\nawait figma.loadFontAsync(shape.text.fontName)\nshape.text.characters = '{text}'\n```"
367
 
368
  comment_data = {
369
  "message": comment_text,
 
378
  if "error" in result:
379
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
380
 
381
+ return f"✅ Instructions forme {shape_type} avec texte créées à ({x_pos}, {y_pos})"
382
 
383
  except ValueError:
384
  return "❌ Les coordonnées et dimensions doivent être des nombres"
385
 
386
+ def create_figjam_table(rows: str, columns: str, x: str = "0", y: str = "0") -> str:
387
+ """Crée un tableau dans FigJam (API officielle)"""
388
  if not figma_config["file_id"]:
389
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
390
 
391
  try:
392
+ num_rows, num_cols = int(rows), int(columns)
393
+ x_pos, y_pos = float(x), float(y)
394
 
395
+ comment_text = f"📊 **Tableau FigJam (API officielle) :**\n- Position: ({x_pos}, {y_pos})\n- Dimensions: {num_rows} lignes × {num_cols} colonnes\n\n📋 **Code Plugin Figma :**\n```javascript\nconst table = figma.createTable({num_rows}, {num_cols})\ntable.x = {x_pos}\ntable.y = {y_pos}\n\n// Exemple: ajouter du texte dans les cellules\nawait figma.loadFontAsync(table.cellAt(0, 0).text.fontName)\ntable.cellAt(0, 0).text.characters = 'Titre 1'\ntable.cellAt(0, 1).text.characters = 'Titre 2'\n```"
396
 
397
  comment_data = {
398
  "message": comment_text,
399
  "client_meta": {
400
+ "x": x_pos,
401
+ "y": y_pos
402
  }
403
  }
404
 
 
407
  if "error" in result:
408
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
409
 
410
+ return f"✅ Instructions tableau {num_rows}×{num_cols} créées à ({x_pos}, {y_pos})"
411
 
412
  except ValueError:
413
+ return "❌ Les dimensions et coordonnées doivent être des nombres"
414
 
415
+ def create_figjam_code_block(x: str, y: str, code: str, language: str = "javascript") -> str:
416
+ """Crée un bloc de code dans FigJam (API officielle)"""
417
  if not figma_config["file_id"]:
418
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
419
 
420
  try:
421
  x_pos, y_pos = float(x), float(y)
 
 
 
 
 
 
 
 
 
 
422
 
423
+ # Échapper les caractères spéciaux pour l'affichage
424
+ escaped_code = code.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
425
 
426
+ comment_text = f"💻 **Bloc de code FigJam (API officielle) :**\n- Position: ({x_pos}, {y_pos})\n- Langage: {language}\n- Code: \"{code[:100]}{'...' if len(code) > 100 else ''}\"\n\n📋 **Code Plugin Figma :**\n```javascript\nconst codeBlock = figma.createCodeBlock()\ncodeBlock.x = {x_pos}\ncodeBlock.y = {y_pos}\nawait figma.loadFontAsync(codeBlock.codeBlockText.fontName)\ncodeBlock.codeBlockText.characters = \"{escaped_code}\"\n```"
427
 
428
  comment_data = {
429
  "message": comment_text,
 
438
  if "error" in result:
439
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
440
 
441
+ return f"✅ Instructions bloc de code {language} créées à ({x_pos}, {y_pos})"
442
 
443
  except ValueError:
444
+ return "❌ Les coordonnées doivent être des nombres"
445
 
446
+ def create_figjam_background_shape(x: str, y: str, width: str, height: str, color: str = "#F3F4F6", title: str = "", corner_radius: str = "8") -> str:
447
+ """Crée une forme de fond rectangulaire pour organiser le contenu FigJam"""
448
  if not figma_config["file_id"]:
449
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
450
 
451
  try:
452
+ x_pos, y_pos = float(x), float(y)
453
+ w, h = float(width), float(height)
454
+ radius = float(corner_radius)
455
 
456
+ comment_text = f"📐 **Forme de fond FigJam (zone de travail) :**\n- Position: ({x_pos}, {y_pos})\n- Taille: {w}x{h}px\n- Couleur: {color}\n- Titre: \"{title}\"\n- Rayon coins: {radius}px\n\n📋 **Code Plugin Figma :**\n```javascript\n// Créer la forme de fond\nconst background = figma.createRectangle()\nbackground.x = {x_pos}\nbackground.y = {y_pos}\nbackground.resize({w}, {h})\nbackground.cornerRadius = {radius}\nbackground.fills = [{{\n type: 'SOLID',\n color: {{ r: 0.95, g: 0.96, b: 0.97 }} // {color}\n}}]\nbackground.name = 'Zone - {title}'\n\n// Optionnel: ajouter un titre\nif ('{title}') {{\n const titleText = figma.createText()\n titleText.x = {x_pos + 16}\n titleText.y = {y_pos + 16}\n await figma.loadFontAsync(titleText.fontName)\n titleText.characters = '{title}'\n titleText.fontSize = 18\n}}\n```"
457
 
458
  comment_data = {
459
  "message": comment_text,
 
468
  if "error" in result:
469
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
470
 
471
+ return f"✅ Zone de travail \"{title}\" créée à ({x_pos}, {y_pos}) - {w}x{h}px"
472
 
473
  except ValueError:
474
+ return "❌ Les coordonnées et dimensions doivent être des nombres"
475
 
476
+ def create_figjam_sticker(x: str, y: str, sticker_type: str, size: str = "40") -> str:
477
+ """Crée un sticker/emoji dans FigJam pour les réactions et annotations"""
478
  if not figma_config["file_id"]:
479
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
480
 
481
  try:
482
+ x_pos, y_pos = float(x), float(y)
483
+ sticker_size = float(size)
484
+
485
+ # Mapping des stickers populaires
486
+ stickers = {
487
+ "thumbs_up": "👍",
488
+ "thumbs_down": "👎",
489
+ "heart": "❤️",
490
+ "star": "⭐",
491
+ "fire": "🔥",
492
+ "rocket": "🚀",
493
+ "bulb": "💡",
494
+ "warning": "⚠️",
495
+ "check": "✅",
496
+ "cross": "❌",
497
+ "question": "❓",
498
+ "idea": "💭",
499
+ "clock": "⏰",
500
+ "money": "💰",
501
+ "target": "🎯",
502
+ "celebrate": "🎉",
503
+ "thinking": "🤔",
504
+ "confused": "😕"
505
+ }
506
 
507
+ emoji = stickers.get(sticker_type.lower(), sticker_type)
508
+
509
+ comment_text = f"🎭 **Sticker FigJam :**\n- Position: ({x_pos}, {y_pos})\n- Type: {sticker_type} → {emoji}\n- Taille: {sticker_size}px\n\n📋 **Code Plugin Figma :**\n```javascript\n// Créer un sticker comme texte stylisé\nconst sticker = figma.createText()\nsticker.x = {x_pos}\nsticker.y = {y_pos}\nsticker.resize({sticker_size}, {sticker_size})\nawait figma.loadFontAsync({{ family: 'Inter', style: 'Regular' }})\nsticker.characters = '{emoji}'\nsticker.fontSize = {sticker_size}\nsticker.textAlignHorizontal = 'CENTER'\nsticker.textAlignVertical = 'CENTER'\nsticker.name = 'Sticker - {sticker_type}'\n```"
510
 
511
  comment_data = {
512
  "message": comment_text,
513
  "client_meta": {
514
+ "x": x_pos,
515
+ "y": y_pos
516
  }
517
  }
518
 
 
521
  if "error" in result:
522
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
523
 
524
+ return f"✅ Sticker {emoji} créé à ({x_pos}, {y_pos}) - Taille: {sticker_size}px"
525
 
526
  except ValueError:
527
+ return "❌ Les coordonnées et la taille doivent être des nombres"
528
 
529
+ def create_figjam_workshop_template(template_type: str, x: str = "0", y: str = "0") -> str:
530
+ """Crée des templates d'atelier FigJam prêts à utiliser"""
531
  if not figma_config["file_id"]:
532
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
533
 
534
  try:
535
+ x_pos, y_pos = float(x), float(y)
536
 
537
+ templates = {
538
+ "retrospective": {
539
+ "title": "Rétrospective Sprint",
540
+ "zones": [
541
+ {"title": "😊 Ce qui s'est bien passé", "x": 0, "y": 100, "width": 300, "height": 400, "color": "#D1FAE5"},
542
+ {"title": "😐 Ce qui peut être amélioré", "x": 320, "y": 100, "width": 300, "height": 400, "color": "#FEF3C7"},
543
+ {"title": "🚀 Actions à prendre", "x": 640, "y": 100, "width": 300, "height": 400, "color": "#DBEAFE"}
544
+ ]
545
+ },
546
+ "brainstorm": {
547
+ "title": "Session Brainstorming",
548
+ "zones": [
549
+ {"title": "🎯 Objectif", "x": 0, "y": 100, "width": 960, "height": 120, "color": "#F3E8FF"},
550
+ {"title": "💡 Idées", "x": 0, "y": 240, "width": 480, "height": 400, "color": "#FEF3C7"},
551
+ {"title": "⭐ Meilleures idées", "x": 500, "y": 240, "width": 460, "height": 400, "color": "#D1FAE5"}
552
+ ]
553
+ },
554
+ "user_journey": {
555
+ "title": "Parcours Utilisateur",
556
+ "zones": [
557
+ {"title": "👤 Persona", "x": 0, "y": 100, "width": 200, "height": 300, "color": "#F3E8FF"},
558
+ {"title": "🎯 Découverte", "x": 220, "y": 100, "width": 180, "height": 300, "color": "#DBEAFE"},
559
+ {"title": "🔍 Exploration", "x": 420, "y": 100, "width": 180, "height": 300, "color": "#FEF3C7"},
560
+ {"title": "✅ Conversion", "x": 620, "y": 100, "width": 180, "height": 300, "color": "#D1FAE5"},
561
+ {"title": "💝 Fidélisation", "x": 820, "y": 100, "width": 180, "height": 300, "color": "#FECACA"}
562
+ ]
563
+ }
564
+ }
565
+
566
+ template = templates.get(template_type.lower())
567
+
568
+ if not template:
569
+ available = ", ".join(templates.keys())
570
+ return f"❌ Template non trouvé. Disponibles: {available}"
571
+
572
+ # Générer le code pour créer le template complet
573
+ zones_code = []
574
+ for i, zone in enumerate(template["zones"]):
575
+ zone_x = x_pos + zone["x"]
576
+ zone_y = y_pos + zone["y"]
577
+ zones_code.append(f"""
578
+ // Zone {i+1}: {zone['title']}
579
+ const zone{i+1} = figma.createRectangle()
580
+ zone{i+1}.x = {zone_x}
581
+ zone{i+1}.y = {zone_y}
582
+ zone{i+1}.resize({zone['width']}, {zone['height']})
583
+ zone{i+1}.cornerRadius = 8
584
+ zone{i+1}.fills = [{{ type: 'SOLID', color: {{ r: 0.95, g: 0.96, b: 0.97 }} }}]
585
+ zone{i+1}.name = '{zone['title']}'
586
+
587
+ const title{i+1} = figma.createText()
588
+ title{i+1}.x = {zone_x + 16}
589
+ title{i+1}.y = {zone_y + 16}
590
+ await figma.loadFontAsync(title{i+1}.fontName)
591
+ title{i+1}.characters = '{zone['title']}'
592
+ title{i+1}.fontSize = 16""")
593
+
594
+ comment_text = f"🏗️ **Template FigJam: {template['title']} :**\n- Position: ({x_pos}, {y_pos})\n- {len(template['zones'])} zones de travail\n\n📋 **Code Plugin Figma :**\n```javascript\n// Template: {template['title']}{''.join(zones_code)}\n```"
595
 
596
  comment_data = {
597
  "message": comment_text,
598
  "client_meta": {
599
+ "x": x_pos,
600
+ "y": y_pos
601
  }
602
  }
603
 
 
606
  if "error" in result:
607
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
608
 
609
+ return f"✅ Template \"{template['title']}\" créé avec {len(template['zones'])} zones à ({x_pos}, {y_pos})"
610
 
611
  except ValueError:
612
+ return "❌ Les coordonnées doivent être des nombres"
613
 
614
+ def create_figjam_organized_zone(title: str, x: str, y: str, width: str = "400", height: str = "500", max_stickies: str = "12") -> str:
615
+ """Crée une zone organisée avec grille pour post-its dans FigJam"""
616
  if not figma_config["file_id"]:
617
  return "❌ ID du fichier requis. Utilisez configure_figma_file_id() d'abord."
618
 
619
  try:
620
  x_pos, y_pos = float(x), float(y)
621
  w, h = float(width), float(height)
622
+ max_notes = int(max_stickies)
623
+
624
+ # Calculer la grille optimale
625
+ cols = 3 if max_notes <= 9 else 4
626
+ rows = (max_notes + cols - 1) // cols # Division avec arrondi vers le haut
627
+
628
+ sticky_width = (w - 80) // cols # 80px de marge totale
629
+ sticky_height = 120 # Hauteur standard d'un post-it
630
 
631
+ comment_text = f"📋 **Zone organisée FigJam :**\n- Titre: \"{title}\"\n- Position: ({x_pos}, {y_pos})\n- Taille: {w}x{h}px\n- Grille: {cols}×{rows} ({max_notes} post-its max)\n\n📋 **Code Plugin Figma :**\n```javascript\n// Zone de fond\nconst zone = figma.createRectangle()\nzone.x = {x_pos}\nzone.y = {y_pos}\nzone.resize({w}, {h})\nzone.cornerRadius = 12\nzone.fills = [{{ type: 'SOLID', color: {{ r: 0.98, g: 0.98, b: 0.99 }} }}]\nzone.strokes = [{{ type: 'SOLID', color: {{ r: 0.9, g: 0.9, b: 0.92 }} }}]\nzone.strokeWeight = 2\nzone.name = 'Zone - {title}'\n\n// Titre\nconst titleText = figma.createText()\ntitleText.x = {x_pos + 20}\ntitleText.y = {y_pos + 20}\nawait figma.loadFontAsync(titleText.fontName)\ntitleText.characters = '{title}'\ntitleText.fontSize = 20\ntitleText.fontName = {{ family: 'Inter', style: 'Bold' }}\n\n// Grille de post-its exemple\nfor (let row = 0; row < {rows}; row++) {{\n for (let col = 0; col < {cols}; col++) {{\n const index = row * {cols} + col\n if (index >= {max_notes}) break\n \n const sticky = figma.createSticky()\n sticky.x = {x_pos + 20} + col * ({sticky_width} + 10)\n sticky.y = {y_pos + 70} + row * ({sticky_height} + 10)\n sticky.resize({sticky_width}, {sticky_height})\n await figma.loadFontAsync(sticky.text.fontName)\n sticky.text.characters = `Idée ${{index + 1}}`\n sticky.name = `Sticky ${{index + 1}}`\n }}\n}}\n```"
632
 
633
  comment_data = {
634
  "message": comment_text,
 
643
  if "error" in result:
644
  return f"❌ Erreur lors de la création du commentaire : {result['error']}"
645
 
646
+ return f"✅ Zone organisée \"{title}\" créée avec grille {cols}×{rows} pour {max_notes} post-its"
647
 
648
  except ValueError:
649
  return "❌ Les coordonnées et dimensions doivent être des nombres"
 
663
  def test_user():
664
  return get_figma_user_info()
665
 
 
 
 
 
 
 
666
  with gr.Blocks(
667
  title="🎨 Figma MCP Server",
668
  theme=gr.themes.Soft(),
 
717
 
718
  # Actions de test
719
  with gr.Row():
 
720
  test_info_btn = gr.Button("📄 Info Fichier")
721
  test_comments_btn = gr.Button("📝 Commentaires")
722
  test_user_btn = gr.Button("👤 Info Utilisateur")
 
723
 
724
  # Connexions des événements
725
  token_btn.click(
 
734
  outputs=[status_output]
735
  )
736
 
 
 
 
 
 
737
  test_info_btn.click(
738
  test_file_info,
739
  outputs=[status_output]
 
748
  test_user,
749
  outputs=[status_output]
750
  )
 
 
 
 
 
751
 
752
  gr.Markdown("""
753
  ---
 
758
  - `configure_figma_file_id(file_id)` - Configure l'ID du fichier
759
 
760
  **📁 Navigation :**
 
761
  - `get_figma_file_info()` - Récupère les infos du fichier
762
  - `get_figma_comments()` - Récupère les commentaires
763
  - `get_figma_user_info()` - Info utilisateur connecté
 
764
 
765
+ **🎨 Création d'éléments de base (Figma Design) :**
766
  - `create_figma_rectangle(x, y, width, height, name, color)` - Crée un rectangle
767
  - `create_figma_frame(x, y, width, height, name)` - Crée un frame
768
  - `create_figma_text(x, y, text, name, font_size)` - Crée un texte
 
 
 
 
 
 
 
 
 
 
769
 
770
+ **🟡 FigJam - Éléments de base :**
771
+ - `create_figjam_sticky_note(x, y, text, width, height)` - Post-it (défaut: 240×240px)
772
+ - `create_figjam_connector_between_elements(element1_name, element2_name, style)` - Connecteur entre éléments
773
+ - `create_figjam_shape_with_text(x, y, shape_type, text, width, height)` - Forme avec texte (défaut: 208×208px)
774
+ - `create_figjam_table(rows, columns, x, y)` - Tableau interactif
775
+ - `create_figjam_code_block(x, y, code, language)` - Bloc de code avec coloration syntaxique
776
+
777
+ **📐 FigJam - Zones et organisation :**
778
+ - `create_figjam_background_shape(x, y, width, height, color, title, corner_radius)` - Zone de fond rectangulaire
779
+ - `create_figjam_organized_zone(title, x, y, width, height, max_stickies)` - Zone avec grille de post-its
780
+ - `create_figjam_workshop_template(template_type, x, y)` - Templates d'atelier complets
781
+
782
+ **🎭 FigJam - Stickers et réactions :**
783
+ - `create_figjam_sticker(x, y, sticker_type, size)` - Stickers/emoji pour réactions
784
+
785
+ **🎯 Types disponibles :**
786
+ - **Formes:** rectangle, circle, triangle, diamond, star, hexagon
787
+ - **Stickers:** thumbs_up, thumbs_down, heart, star, fire, rocket, bulb, warning, check, cross, question, idea, clock, money, target, celebrate, thinking, confused
788
+ - **Templates:** retrospective, brainstorm, user_journey
789
+
790
+ **💡 Workflow recommandé :**
791
+ 1. Créer une zone de fond avec `create_figjam_background_shape()`
792
+ 2. Ajouter des post-its avec `create_figjam_sticky_note()`
793
+ 3. Connecter les éléments avec `create_figjam_connector_between_elements()`
794
+ 4. Ajouter des stickers pour les réactions avec `create_figjam_sticker()`
795
+
796
+ **✨ Toutes les fonctions génèrent du code JavaScript prêt à utiliser dans un plugin Figma !**
797
  """)
798
 
799
  return demo