Upload 3 files
Browse files- interface_wizard.py +693 -0
- text_analyzer.py +187 -0
- wizard_style.css +222 -0
interface_wizard.py
ADDED
|
@@ -0,0 +1,693 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Module définissant l'interface utilisateur Gradio sous forme d'assistant progressif (wizard).
|
| 3 |
+
Cette version permet une navigation par étapes avec aperçu en temps réel.
|
| 4 |
+
"""
|
| 5 |
+
import gradio as gr
|
| 6 |
+
from utils import collect_author_info, ensure_default_supports
|
| 7 |
+
from text_analyzer import analyze_work_description, get_explanation
|
| 8 |
+
from config import (CONTRACT_TYPES, CESSION_MODES, ADDITIONAL_RIGHTS,
|
| 9 |
+
AUTHOR_TYPES, CIVILITY_OPTIONS, SUPPORTS_OPTIONS)
|
| 10 |
+
import time
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def create_wizard_interface(generate_pdf_fn, preview_contract_fn):
|
| 14 |
+
"""
|
| 15 |
+
Crée l'interface utilisateur Gradio avec navigation progressive par étapes.
|
| 16 |
+
|
| 17 |
+
Args:
|
| 18 |
+
generate_pdf_fn: Fonction pour générer le PDF
|
| 19 |
+
preview_contract_fn: Fonction pour prévisualiser le contrat
|
| 20 |
+
|
| 21 |
+
Returns:
|
| 22 |
+
gr.Blocks: L'interface Gradio configurée
|
| 23 |
+
"""
|
| 24 |
+
# Définir le nombre total d'étapes
|
| 25 |
+
TOTAL_STEPS = 6
|
| 26 |
+
|
| 27 |
+
with gr.Blocks(title="Assistant de Contrats de Cession", css="style.css") as demo:
|
| 28 |
+
# Variables d'état pour stocker les données entre les étapes
|
| 29 |
+
current_step = gr.State(value=1)
|
| 30 |
+
contract_data = gr.State(value={
|
| 31 |
+
"type_contrat": [],
|
| 32 |
+
"type_cession": "Gratuite",
|
| 33 |
+
"droits_cedes": [],
|
| 34 |
+
"exclusivite": False,
|
| 35 |
+
"auteur_type": "Personne physique",
|
| 36 |
+
"auteur_info": {},
|
| 37 |
+
"description_oeuvre": "",
|
| 38 |
+
"description_image": "",
|
| 39 |
+
"supports": [],
|
| 40 |
+
"remuneration": ""
|
| 41 |
+
})
|
| 42 |
+
|
| 43 |
+
gr.Markdown("# Assistant de Création de Contrat de Cession")
|
| 44 |
+
gr.Markdown("Cet assistant vous guide pas à pas dans la création d'un contrat adapté à vos besoins spécifiques.")
|
| 45 |
+
|
| 46 |
+
# Layout principal en deux colonnes
|
| 47 |
+
with gr.Row():
|
| 48 |
+
# COLONNE GAUCHE - FORMULAIRE PROGRESSIF
|
| 49 |
+
with gr.Column(scale=3):
|
| 50 |
+
# Indicateur de progression
|
| 51 |
+
progress_bar = gr.Slider(
|
| 52 |
+
minimum=1,
|
| 53 |
+
maximum=TOTAL_STEPS,
|
| 54 |
+
value=1,
|
| 55 |
+
step=1,
|
| 56 |
+
interactive=False,
|
| 57 |
+
label="Progression"
|
| 58 |
+
)
|
| 59 |
+
progress_text = gr.Markdown("**Étape 1 sur 6**: Type d'œuvre")
|
| 60 |
+
|
| 61 |
+
# ===== ÉTAPE 1: DESCRIPTION ET TYPE DE CONTRAT =====
|
| 62 |
+
with gr.Group(visible=True) as step1_group:
|
| 63 |
+
gr.Markdown("## Décrivez votre projet")
|
| 64 |
+
gr.Markdown("""
|
| 65 |
+
Décrivez en quelques mots l'œuvre ou le contenu pour lequel vous souhaitez établir un contrat.
|
| 66 |
+
Exemples: "Une chanson que j'ai composée", "Des photos de mannequins", "Un logo pour une entreprise", etc.
|
| 67 |
+
""")
|
| 68 |
+
|
| 69 |
+
project_description = gr.Textbox(
|
| 70 |
+
label="Description de votre projet",
|
| 71 |
+
placeholder="Ex: Une vidéo où je me filme en train de jouer ma composition au piano",
|
| 72 |
+
lines=3
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
analyze_btn = gr.Button("Analyser mon projet", variant="secondary")
|
| 76 |
+
|
| 77 |
+
contract_type_suggestion = gr.Markdown(
|
| 78 |
+
value="Complétez la description et cliquez sur 'Analyser mon projet' pour obtenir une suggestion.",
|
| 79 |
+
elem_id="contract-suggestion"
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
gr.Markdown("### Type de contrat nécessaire")
|
| 83 |
+
contract_type = gr.CheckboxGroup(
|
| 84 |
+
CONTRACT_TYPES,
|
| 85 |
+
label="Sélectionnez le(s) type(s) de contrat",
|
| 86 |
+
value=[]
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
# ===== ÉTAPE 2: MODE DE CESSION ET DROITS =====
|
| 90 |
+
with gr.Group(visible=False) as step2_group:
|
| 91 |
+
gr.Markdown("## Mode de cession et droits")
|
| 92 |
+
|
| 93 |
+
gr.Markdown("### Mode de cession")
|
| 94 |
+
gr.Markdown("""
|
| 95 |
+
La cession peut se faire à titre gratuit ou onéreux (moyennant rémunération).
|
| 96 |
+
Une cession gratuite limite les droits cédés aux droits de base (reproduction et représentation).
|
| 97 |
+
""")
|
| 98 |
+
|
| 99 |
+
cession_mode = gr.Radio(
|
| 100 |
+
CESSION_MODES,
|
| 101 |
+
label="La cession se fait-elle à titre gratuit ou onéreux?",
|
| 102 |
+
value="Gratuite"
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
# Droits cédés (visible uniquement si onéreux)
|
| 106 |
+
with gr.Group(visible=False) as group_rights:
|
| 107 |
+
gr.Markdown("### Droits supplémentaires (cession onéreuse)")
|
| 108 |
+
gr.Markdown("""
|
| 109 |
+
Pour une cession onéreuse, vous pouvez céder des droits supplémentaires.
|
| 110 |
+
Les droits de reproduction et de représentation sont toujours inclus.
|
| 111 |
+
""")
|
| 112 |
+
|
| 113 |
+
additional_rights = gr.CheckboxGroup(
|
| 114 |
+
ADDITIONAL_RIGHTS,
|
| 115 |
+
label="Sélectionnez les droits supplémentaires à céder",
|
| 116 |
+
value=[]
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
gr.Markdown("### Exclusivité")
|
| 120 |
+
gr.Markdown("""
|
| 121 |
+
L'exclusivité signifie que le cédant ne pourra pas exploiter lui-même l'œuvre
|
| 122 |
+
ni céder les mêmes droits à d'autres personnes pendant la durée du contrat.
|
| 123 |
+
""")
|
| 124 |
+
|
| 125 |
+
exclusivity = gr.Checkbox(
|
| 126 |
+
label="Cession exclusive",
|
| 127 |
+
value=False,
|
| 128 |
+
info="Cochez cette case pour une cession exclusive"
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
# ===== ÉTAPE 3: INFORMATIONS SUR L'AUTEUR/MODÈLE =====
|
| 132 |
+
with gr.Group(visible=False) as step3_group:
|
| 133 |
+
gr.Markdown("## Informations sur l'auteur/modèle")
|
| 134 |
+
|
| 135 |
+
author_type = gr.Radio(
|
| 136 |
+
AUTHOR_TYPES,
|
| 137 |
+
label="L'auteur/modèle est:",
|
| 138 |
+
value="Personne physique"
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
# Personne physique
|
| 142 |
+
with gr.Group() as group_physical_person:
|
| 143 |
+
civility = gr.Radio(
|
| 144 |
+
CIVILITY_OPTIONS,
|
| 145 |
+
label="Civilité",
|
| 146 |
+
value="M."
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
with gr.Row():
|
| 150 |
+
last_name = gr.Textbox(
|
| 151 |
+
label="Nom",
|
| 152 |
+
placeholder="Nom de famille"
|
| 153 |
+
)
|
| 154 |
+
first_name = gr.Textbox(
|
| 155 |
+
label="Prénom",
|
| 156 |
+
placeholder="Prénom"
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
with gr.Row():
|
| 160 |
+
birth_date = gr.Textbox(
|
| 161 |
+
label="Date de naissance (facultatif)",
|
| 162 |
+
placeholder="JJ/MM/AAAA"
|
| 163 |
+
)
|
| 164 |
+
nationality = gr.Textbox(
|
| 165 |
+
label="Nationalité",
|
| 166 |
+
placeholder="Ex: française"
|
| 167 |
+
)
|
| 168 |
+
|
| 169 |
+
address = gr.Textbox(
|
| 170 |
+
label="Adresse complète",
|
| 171 |
+
placeholder="Numéro, rue, code postal, ville"
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
contact_physical = gr.Textbox(
|
| 175 |
+
label="Moyen de contact (email, téléphone)",
|
| 176 |
+
placeholder="Email et/ou téléphone"
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
# Personne morale
|
| 180 |
+
with gr.Group(visible=False) as group_legal_entity:
|
| 181 |
+
company_name = gr.Textbox(
|
| 182 |
+
label="Nom de la société",
|
| 183 |
+
placeholder="Dénomination sociale"
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
with gr.Row():
|
| 187 |
+
legal_status = gr.Textbox(
|
| 188 |
+
label="Statut juridique",
|
| 189 |
+
placeholder="Ex: SARL, SAS, EURL, etc."
|
| 190 |
+
)
|
| 191 |
+
rcs_number = gr.Textbox(
|
| 192 |
+
label="Numéro RCS",
|
| 193 |
+
placeholder="Ex: 123 456 789 R.C.S. Paris"
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
company_address = gr.Textbox(
|
| 197 |
+
label="Adresse du siège social",
|
| 198 |
+
placeholder="Adresse complète du siège"
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
contact_company = gr.Textbox(
|
| 202 |
+
label="Moyen de contact (email, téléphone)",
|
| 203 |
+
placeholder="Email et/ou téléphone"
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
# ===== ÉTAPE 4: DESCRIPTION DE L'ŒUVRE/IMAGE =====
|
| 207 |
+
with gr.Group(visible=False) as step4_group:
|
| 208 |
+
description_title = gr.Markdown("## Description détaillée")
|
| 209 |
+
|
| 210 |
+
# Description de l'œuvre (visible si contrat de droits d'auteur)
|
| 211 |
+
with gr.Group(visible=True) as group_work_description:
|
| 212 |
+
gr.Markdown("### Description de l'œuvre")
|
| 213 |
+
gr.Markdown("""
|
| 214 |
+
Décrivez précisément l'œuvre concernée par la cession de droits.
|
| 215 |
+
Cette description sera intégrée dans le contrat pour identifier sans ambiguïté l'objet de la cession.
|
| 216 |
+
""")
|
| 217 |
+
|
| 218 |
+
work_description = gr.Textbox(
|
| 219 |
+
label="Description de l'œuvre",
|
| 220 |
+
placeholder="Titre, format, dimensions, support, technique utilisée, date de création, etc.",
|
| 221 |
+
lines=5
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
# Description de l'image (visible si contrat de droits à l'image)
|
| 225 |
+
with gr.Group(visible=False) as group_image_description:
|
| 226 |
+
gr.Markdown("### Description des images")
|
| 227 |
+
gr.Markdown("""
|
| 228 |
+
Décrivez précisément les images ou vidéos concernées par la cession du droit à l'image.
|
| 229 |
+
Précisez le contexte, la date et le lieu de prise de vue, le nombre d'images concernées, etc.
|
| 230 |
+
""")
|
| 231 |
+
|
| 232 |
+
image_description = gr.Textbox(
|
| 233 |
+
label="Description des images/vidéos",
|
| 234 |
+
placeholder="Ex: Séance photo réalisée le [date] à [lieu], comprenant X photographies où apparaît [nom du modèle]",
|
| 235 |
+
lines=5
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
# ===== ÉTAPE 5: SUPPORTS D'EXPLOITATION =====
|
| 239 |
+
with gr.Group(visible=False) as step5_group:
|
| 240 |
+
gr.Markdown("## Supports d'exploitation")
|
| 241 |
+
gr.Markdown("""
|
| 242 |
+
Sélectionnez les supports sur lesquels l'œuvre et/ou l'image pourra être exploitée.
|
| 243 |
+
Le site web et Discord de Tellers sont automatiquement inclus.
|
| 244 |
+
""")
|
| 245 |
+
|
| 246 |
+
exploitation_supports = gr.CheckboxGroup(
|
| 247 |
+
SUPPORTS_OPTIONS,
|
| 248 |
+
label="Sur quels supports les droits seront-ils exploités?",
|
| 249 |
+
value=["Réseaux sociaux (Facebook, Instagram, Twitter, etc.)"]
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
# Rémunération (visible uniquement si onéreux)
|
| 253 |
+
with gr.Group(visible=False) as group_remuneration:
|
| 254 |
+
gr.Markdown("### Rémunération")
|
| 255 |
+
gr.Markdown("""
|
| 256 |
+
Précisez les modalités de rémunération pour cette cession onéreuse.
|
| 257 |
+
Cela peut être un montant forfaitaire ou proportionnel aux recettes.
|
| 258 |
+
""")
|
| 259 |
+
|
| 260 |
+
remuneration_details = gr.Textbox(
|
| 261 |
+
label="Modalités de rémunération",
|
| 262 |
+
placeholder="Ex: 500€ versés à la signature, 5% des recettes versés trimestriellement",
|
| 263 |
+
lines=3
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
# ===== ÉTAPE 6: VALIDATION ET GÉNÉRATION =====
|
| 267 |
+
with gr.Group(visible=False) as step6_group:
|
| 268 |
+
gr.Markdown("## Validation et génération du contrat")
|
| 269 |
+
gr.Markdown("""
|
| 270 |
+
Vous avez complété toutes les étapes nécessaires.
|
| 271 |
+
Vérifiez le contrat dans l'aperçu à droite, puis générez le PDF final.
|
| 272 |
+
""")
|
| 273 |
+
|
| 274 |
+
gr.Markdown("### Options de génération")
|
| 275 |
+
contract_name = gr.Textbox(
|
| 276 |
+
label="Nom du fichier PDF (optionnel)",
|
| 277 |
+
placeholder="Ex: Contrat_Cession_Dupont_2025",
|
| 278 |
+
value=""
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
# Indicateur de génération
|
| 282 |
+
with gr.Group(visible=False) as generation_status_group:
|
| 283 |
+
generation_status = gr.Markdown("Préparation du contrat en cours...")
|
| 284 |
+
generation_progress = gr.Slider(
|
| 285 |
+
minimum=0,
|
| 286 |
+
maximum=100,
|
| 287 |
+
value=0,
|
| 288 |
+
step=1,
|
| 289 |
+
interactive=False,
|
| 290 |
+
label="Progression"
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
# Boutons de navigation entre les étapes
|
| 294 |
+
with gr.Row():
|
| 295 |
+
back_button = gr.Button("Précédent", variant="secondary")
|
| 296 |
+
next_button = gr.Button("Suivant", variant="primary")
|
| 297 |
+
|
| 298 |
+
# Bouton de génération (visible uniquement à la dernière étape)
|
| 299 |
+
with gr.Row(visible=False) as generate_button_row:
|
| 300 |
+
generate_button = gr.Button("Générer le PDF", variant="primary", elem_id="generate-btn")
|
| 301 |
+
|
| 302 |
+
# COLONNE DROITE - PRÉVISUALISATION EN TEMPS RÉEL
|
| 303 |
+
with gr.Column(scale=2):
|
| 304 |
+
# En-tête de prévisualisation
|
| 305 |
+
preview_header = gr.Markdown("## Aperçu du contrat en temps réel")
|
| 306 |
+
preview_info = gr.Markdown(
|
| 307 |
+
"Au fur et à mesure que vous remplissez le formulaire, votre contrat se construit ici."
|
| 308 |
+
)
|
| 309 |
+
|
| 310 |
+
# Prévisualisation du contrat
|
| 311 |
+
contract_preview = gr.Markdown(
|
| 312 |
+
value="*Commencez à remplir le formulaire pour voir l'aperçu du contrat*",
|
| 313 |
+
elem_id="contract-preview"
|
| 314 |
+
)
|
| 315 |
+
|
| 316 |
+
# Zone de téléchargement (visible uniquement après génération)
|
| 317 |
+
with gr.Group(visible=False) as download_group:
|
| 318 |
+
gr.Markdown("### Téléchargement")
|
| 319 |
+
pdf_output = gr.File(label="Votre contrat est prêt!")
|
| 320 |
+
|
| 321 |
+
# ===== FONCTIONS DE NAVIGATION ET MISE À JOUR =====
|
| 322 |
+
|
| 323 |
+
# Fonction pour mettre à jour l'indicateur de progression
|
| 324 |
+
def update_progress(step):
|
| 325 |
+
progress_text_value = f"**Étape {step} sur {TOTAL_STEPS}**: "
|
| 326 |
+
|
| 327 |
+
if step == 1:
|
| 328 |
+
progress_text_value += "Type d'œuvre"
|
| 329 |
+
elif step == 2:
|
| 330 |
+
progress_text_value += "Mode de cession et droits"
|
| 331 |
+
elif step == 3:
|
| 332 |
+
progress_text_value += "Informations sur l'auteur/modèle"
|
| 333 |
+
elif step == 4:
|
| 334 |
+
progress_text_value += "Description détaillée"
|
| 335 |
+
elif step == 5:
|
| 336 |
+
progress_text_value += "Supports d'exploitation"
|
| 337 |
+
elif step == 6:
|
| 338 |
+
progress_text_value += "Validation et génération"
|
| 339 |
+
|
| 340 |
+
return step, progress_text_value
|
| 341 |
+
|
| 342 |
+
# Fonction pour analyser la description et suggérer le type de contrat
|
| 343 |
+
def analyze_project(description):
|
| 344 |
+
"""Analyse la description et suggère le type de contrat approprié."""
|
| 345 |
+
if not description.strip():
|
| 346 |
+
return "Veuillez fournir une description pour obtenir une suggestion.", []
|
| 347 |
+
|
| 348 |
+
detected_types = analyze_work_description(description)
|
| 349 |
+
explanation = get_explanation(detected_types)
|
| 350 |
+
|
| 351 |
+
return explanation, detected_types
|
| 352 |
+
|
| 353 |
+
# Associer le bouton d'analyse à la fonction
|
| 354 |
+
analyze_btn.click(
|
| 355 |
+
fn=analyze_project,
|
| 356 |
+
inputs=[project_description],
|
| 357 |
+
outputs=[contract_type_suggestion, contract_type]
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
# Fonction pour naviguer à l'étape suivante
|
| 361 |
+
def next_step(current, data,
|
| 362 |
+
# Étape 1
|
| 363 |
+
project_desc, contract_types,
|
| 364 |
+
# Étape 2
|
| 365 |
+
cession_type, rights, is_exclusive,
|
| 366 |
+
# Étape 3
|
| 367 |
+
author_type_val, civility_val, last_name_val, first_name_val, birth_date_val,
|
| 368 |
+
nationality_val, address_val, contact_physical_val, company_name_val,
|
| 369 |
+
legal_status_val, rcs_val, company_address_val, contact_company_val,
|
| 370 |
+
# Étape 4
|
| 371 |
+
work_desc, image_desc,
|
| 372 |
+
# Étape 5
|
| 373 |
+
supports_val, remuneration_val):
|
| 374 |
+
"""Passe à l'étape suivante et met à jour les données du contrat."""
|
| 375 |
+
|
| 376 |
+
# Mettre à jour les données en fonction de l'étape actuelle
|
| 377 |
+
if current == 1:
|
| 378 |
+
data["project_description"] = project_desc
|
| 379 |
+
data["type_contrat"] = contract_types
|
| 380 |
+
elif current == 2:
|
| 381 |
+
data["type_cession"] = cession_type
|
| 382 |
+
data["droits_cedes"] = rights if rights else []
|
| 383 |
+
data["exclusivite"] = is_exclusive
|
| 384 |
+
elif current == 3:
|
| 385 |
+
data["auteur_type"] = author_type_val
|
| 386 |
+
|
| 387 |
+
# Recueillir les informations sur l'auteur en fonction du type
|
| 388 |
+
if author_type_val == "Personne physique":
|
| 389 |
+
author_info = {
|
| 390 |
+
"gentille": civility_val,
|
| 391 |
+
"nom": last_name_val,
|
| 392 |
+
"prenom": first_name_val,
|
| 393 |
+
"date_naissance": birth_date_val,
|
| 394 |
+
"nationalite": nationality_val,
|
| 395 |
+
"adresse": address_val,
|
| 396 |
+
"contact": contact_physical_val
|
| 397 |
+
}
|
| 398 |
+
else:
|
| 399 |
+
author_info = {
|
| 400 |
+
"nom_societe": company_name_val,
|
| 401 |
+
"statut": legal_status_val,
|
| 402 |
+
"rcs": rcs_val,
|
| 403 |
+
"siege": company_address_val,
|
| 404 |
+
"contact": contact_company_val
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
data["auteur_info"] = author_info
|
| 408 |
+
elif current == 4:
|
| 409 |
+
data["description_oeuvre"] = work_desc
|
| 410 |
+
data["description_image"] = image_desc
|
| 411 |
+
elif current == 5:
|
| 412 |
+
data["supports"] = supports_val
|
| 413 |
+
data["remuneration"] = remuneration_val
|
| 414 |
+
|
| 415 |
+
# Si c'est la dernière étape, ne pas avancer
|
| 416 |
+
if current >= TOTAL_STEPS:
|
| 417 |
+
current = TOTAL_STEPS
|
| 418 |
+
else:
|
| 419 |
+
current += 1
|
| 420 |
+
|
| 421 |
+
# Visibilité des groupes en fonction de la nouvelle étape
|
| 422 |
+
step1_visibility = (current == 1)
|
| 423 |
+
step2_visibility = (current == 2)
|
| 424 |
+
step3_visibility = (current == 3)
|
| 425 |
+
step4_visibility = (current == 4)
|
| 426 |
+
step5_visibility = (current == 5)
|
| 427 |
+
step6_visibility = (current == 6)
|
| 428 |
+
|
| 429 |
+
# Visibilité conditionnelle des droits supplémentaires et rémunération
|
| 430 |
+
rights_visibility = (current == 2 and cession_type == "Onéreuse")
|
| 431 |
+
remuneration_visibility = (current == 5 and data["type_cession"] == "Onéreuse")
|
| 432 |
+
|
| 433 |
+
# Visibilité des champs de description en fonction du type de contrat
|
| 434 |
+
show_work_desc = True
|
| 435 |
+
show_image_desc = False
|
| 436 |
+
|
| 437 |
+
if current == 4:
|
| 438 |
+
show_work_desc = "Auteur (droits d'auteur)" in data["type_contrat"]
|
| 439 |
+
show_image_desc = "Image (droit à l'image)" in data["type_contrat"]
|
| 440 |
+
|
| 441 |
+
# Visibilité du type de personne
|
| 442 |
+
show_physical_person = (current == 3 and author_type_val == "Personne physique")
|
| 443 |
+
show_legal_entity = (current == 3 and author_type_val == "Personne morale")
|
| 444 |
+
|
| 445 |
+
# Visibilité du bouton de génération (uniquement à la dernière étape)
|
| 446 |
+
show_generate_button = (current == TOTAL_STEPS)
|
| 447 |
+
|
| 448 |
+
# Mettre à jour l'aperçu du contrat
|
| 449 |
+
preview = preview_contract(data)
|
| 450 |
+
|
| 451 |
+
# Mettre à jour la progression
|
| 452 |
+
new_progress, progress_text_val = update_progress(current)
|
| 453 |
+
|
| 454 |
+
return (
|
| 455 |
+
# État mis à jour
|
| 456 |
+
current, data,
|
| 457 |
+
# Progression
|
| 458 |
+
new_progress, progress_text_val,
|
| 459 |
+
# Visibilité des étapes
|
| 460 |
+
gr.update(visible=step1_visibility), gr.update(visible=step2_visibility),
|
| 461 |
+
gr.update(visible=step3_visibility), gr.update(visible=step4_visibility),
|
| 462 |
+
gr.update(visible=step5_visibility), gr.update(visible=step6_visibility),
|
| 463 |
+
# Visibilité conditionnelle
|
| 464 |
+
gr.update(visible=rights_visibility), gr.update(visible=remuneration_visibility),
|
| 465 |
+
gr.update(visible=show_work_desc), gr.update(visible=show_image_desc),
|
| 466 |
+
gr.update(visible=show_physical_person), gr.update(visible=show_legal_entity),
|
| 467 |
+
gr.update(visible=show_generate_button),
|
| 468 |
+
# Aperçu du contrat
|
| 469 |
+
preview
|
| 470 |
+
)
|
| 471 |
+
|
| 472 |
+
# Fonction pour naviguer à l'étape précédente
|
| 473 |
+
def previous_step(current, data):
|
| 474 |
+
"""Revient à l'étape précédente."""
|
| 475 |
+
|
| 476 |
+
if current <= 1:
|
| 477 |
+
current = 1
|
| 478 |
+
else:
|
| 479 |
+
current -= 1
|
| 480 |
+
|
| 481 |
+
# Visibilité des groupes en fonction de la nouvelle étape
|
| 482 |
+
step1_visibility = (current == 1)
|
| 483 |
+
step2_visibility = (current == 2)
|
| 484 |
+
step3_visibility = (current == 3)
|
| 485 |
+
step4_visibility = (current == 4)
|
| 486 |
+
step5_visibility = (current == 5)
|
| 487 |
+
step6_visibility = (current == 6)
|
| 488 |
+
|
| 489 |
+
# Visibilité conditionnelle des droits supplémentaires et rémunération
|
| 490 |
+
rights_visibility = (current == 2 and data["type_cession"] == "Onéreuse")
|
| 491 |
+
remuneration_visibility = (current == 5 and data["type_cession"] == "Onéreuse")
|
| 492 |
+
|
| 493 |
+
# Visibilité des champs de description en fonction du type de contrat
|
| 494 |
+
show_work_desc = True
|
| 495 |
+
show_image_desc = False
|
| 496 |
+
|
| 497 |
+
if current == 4:
|
| 498 |
+
show_work_desc = "Auteur (droits d'auteur)" in data["type_contrat"]
|
| 499 |
+
show_image_desc = "Image (droit à l'image)" in data["type_contrat"]
|
| 500 |
+
|
| 501 |
+
# Visibilité du type de personne
|
| 502 |
+
show_physical_person = (current == 3 and data["auteur_type"] == "Personne physique")
|
| 503 |
+
show_legal_entity = (current == 3 and data["auteur_type"] == "Personne morale")
|
| 504 |
+
|
| 505 |
+
# Visibilité du bouton de génération (uniquement à la dernière étape)
|
| 506 |
+
show_generate_button = (current == TOTAL_STEPS)
|
| 507 |
+
|
| 508 |
+
# Mettre à jour l'aperçu du contrat
|
| 509 |
+
preview = preview_contract(data)
|
| 510 |
+
|
| 511 |
+
# Mettre à jour la progression
|
| 512 |
+
new_progress, progress_text_val = update_progress(current)
|
| 513 |
+
|
| 514 |
+
return (
|
| 515 |
+
# État mis à jour
|
| 516 |
+
current, data,
|
| 517 |
+
# Progression
|
| 518 |
+
new_progress, progress_text_val,
|
| 519 |
+
# Visibilité des étapes
|
| 520 |
+
gr.update(visible=step1_visibility), gr.update(visible=step2_visibility),
|
| 521 |
+
gr.update(visible=step3_visibility), gr.update(visible=step4_visibility),
|
| 522 |
+
gr.update(visible=step5_visibility), gr.update(visible=step6_visibility),
|
| 523 |
+
# Visibilité conditionnelle
|
| 524 |
+
gr.update(visible=rights_visibility), gr.update(visible=remuneration_visibility),
|
| 525 |
+
gr.update(visible=show_work_desc), gr.update(visible=show_image_desc),
|
| 526 |
+
gr.update(visible=show_physical_person), gr.update(visible=show_legal_entity),
|
| 527 |
+
gr.update(visible=show_generate_button),
|
| 528 |
+
# Aperçu du contrat
|
| 529 |
+
preview
|
| 530 |
+
)
|
| 531 |
+
|
| 532 |
+
# Fonction pour mettre à jour l'affichage en fonction du mode de cession
|
| 533 |
+
def update_cession_mode_display(mode):
|
| 534 |
+
"""Met à jour l'affichage des champs liés au mode de cession."""
|
| 535 |
+
is_onereux = (mode == "Onéreuse")
|
| 536 |
+
return gr.update(visible=is_onereux)
|
| 537 |
+
|
| 538 |
+
# Fonction pour mettre à jour l'affichage en fonction du type d'auteur
|
| 539 |
+
def update_author_type_display(type_val):
|
| 540 |
+
"""Met à jour l'affichage des champs liés au type d'auteur."""
|
| 541 |
+
is_physical = (type_val == "Personne physique")
|
| 542 |
+
return gr.update(visible=is_physical), gr.update(visible=not is_physical)
|
| 543 |
+
|
| 544 |
+
# Fonction pour générer le PDF
|
| 545 |
+
def generate_pdf(contract_data, filename):
|
| 546 |
+
"""Génère le PDF du contrat avec indication de progression."""
|
| 547 |
+
|
| 548 |
+
# Mise à jour de l'interface pour indiquer le début de la génération
|
| 549 |
+
yield gr.update(visible=True), gr.update(value="Préparation des données..."), 0, gr.update(visible=False), None
|
| 550 |
+
time.sleep(0.5)
|
| 551 |
+
|
| 552 |
+
# Étape 1: Préparation du contrat (25%)
|
| 553 |
+
yield gr.update(visible=True), gr.update(value="Construction du contrat..."), 25, gr.update(visible=False), None
|
| 554 |
+
time.sleep(0.5)
|
| 555 |
+
|
| 556 |
+
# Étape 2: Mise en forme (50%)
|
| 557 |
+
yield gr.update(visible=True), gr.update(value="Mise en forme du document..."), 50, gr.update(visible=False), None
|
| 558 |
+
time.sleep(0.5)
|
| 559 |
+
|
| 560 |
+
# Étape 3: Génération du PDF (75%)
|
| 561 |
+
yield gr.update(visible=True), gr.update(value="Génération du PDF..."), 75, gr.update(visible=False), None
|
| 562 |
+
|
| 563 |
+
# Appel à la fonction de génération réelle
|
| 564 |
+
pdf_path = generate_pdf_fn(
|
| 565 |
+
contract_data["type_contrat"],
|
| 566 |
+
contract_data["type_cession"],
|
| 567 |
+
contract_data["auteur_type"],
|
| 568 |
+
contract_data["auteur_info"],
|
| 569 |
+
contract_data["description_oeuvre"],
|
| 570 |
+
contract_data["description_image"],
|
| 571 |
+
contract_data["supports"],
|
| 572 |
+
contract_data["droits_cedes"],
|
| 573 |
+
contract_data["remuneration"],
|
| 574 |
+
contract_data["exclusivite"]
|
| 575 |
+
)
|
| 576 |
+
|
| 577 |
+
# Finalisation (100%)
|
| 578 |
+
yield gr.update(visible=True), gr.update(value="Contrat PDF généré avec succès!"), 100, gr.update(visible=True), pdf_path
|
| 579 |
+
|
| 580 |
+
# Fonction simplifiée pour prévisualiser le contrat
|
| 581 |
+
def preview_contract(data):
|
| 582 |
+
"""Génère un aperçu HTML formaté du contrat."""
|
| 583 |
+
|
| 584 |
+
# Vérifier qu'il y a suffisamment de données pour prévisualiser
|
| 585 |
+
if not data.get("type_contrat"):
|
| 586 |
+
return "*Complétez au moins le type de contrat pour voir l'aperçu*"
|
| 587 |
+
|
| 588 |
+
# Appeler la fonction de prévisualisation
|
| 589 |
+
try:
|
| 590 |
+
preview_text = preview_contract_fn(
|
| 591 |
+
data.get("type_contrat", []),
|
| 592 |
+
data.get("type_cession", "Gratuite"),
|
| 593 |
+
data.get("auteur_type", "Personne physique"),
|
| 594 |
+
data.get("auteur_info", {}),
|
| 595 |
+
data.get("description_oeuvre", ""),
|
| 596 |
+
data.get("description_image", ""),
|
| 597 |
+
data.get("supports", []),
|
| 598 |
+
data.get("droits_cedes", []),
|
| 599 |
+
data.get("remuneration", ""),
|
| 600 |
+
data.get("exclusivite", False)
|
| 601 |
+
)
|
| 602 |
+
|
| 603 |
+
# Conversion en HTML avec mise en évidence des données utilisateur
|
| 604 |
+
preview_html = preview_text.replace("\n", "<br>")
|
| 605 |
+
|
| 606 |
+
# Mettre en évidence les titres
|
| 607 |
+
for ligne in preview_text.split("\n"):
|
| 608 |
+
if ligne.strip().startswith("ARTICLE") or ligne.strip().isupper():
|
| 609 |
+
preview_html = preview_html.replace(ligne, f"<h3>{ligne}</h3>")
|
| 610 |
+
|
| 611 |
+
return preview_html
|
| 612 |
+
except Exception as e:
|
| 613 |
+
return f"*Erreur de prévisualisation: {str(e)}*"
|
| 614 |
+
|
| 615 |
+
# ===== ÉVÉNEMENTS =====
|
| 616 |
+
|
| 617 |
+
# Navigation entre les étapes
|
| 618 |
+
next_button.click(
|
| 619 |
+
fn=next_step,
|
| 620 |
+
inputs=[
|
| 621 |
+
current_step, contract_data,
|
| 622 |
+
# Étape 1
|
| 623 |
+
project_description, contract_type,
|
| 624 |
+
# Étape 2
|
| 625 |
+
cession_mode, additional_rights, exclusivity,
|
| 626 |
+
# Étape 3
|
| 627 |
+
author_type, civility, last_name, first_name, birth_date,
|
| 628 |
+
nationality, address, contact_physical, company_name,
|
| 629 |
+
legal_status, rcs_number, company_address, contact_company,
|
| 630 |
+
# Étape 4
|
| 631 |
+
work_description, image_description,
|
| 632 |
+
# Étape 5
|
| 633 |
+
exploitation_supports, remuneration_details
|
| 634 |
+
],
|
| 635 |
+
outputs=[
|
| 636 |
+
current_step, contract_data,
|
| 637 |
+
# Progression
|
| 638 |
+
progress_bar, progress_text,
|
| 639 |
+
# Visibilité des étapes
|
| 640 |
+
step1_group, step2_group, step3_group, step4_group, step5_group, step6_group,
|
| 641 |
+
# Visibilité conditionnelle
|
| 642 |
+
group_rights, group_remuneration,
|
| 643 |
+
group_work_description, group_image_description,
|
| 644 |
+
group_physical_person, group_legal_entity,
|
| 645 |
+
generate_button_row,
|
| 646 |
+
# Aperçu du contrat
|
| 647 |
+
contract_preview
|
| 648 |
+
]
|
| 649 |
+
)
|
| 650 |
+
|
| 651 |
+
back_button.click(
|
| 652 |
+
fn=previous_step,
|
| 653 |
+
inputs=[current_step, contract_data],
|
| 654 |
+
outputs=[
|
| 655 |
+
current_step, contract_data,
|
| 656 |
+
# Progression
|
| 657 |
+
progress_bar, progress_text,
|
| 658 |
+
# Visibilité des étapes
|
| 659 |
+
step1_group, step2_group, step3_group, step4_group, step5_group, step6_group,
|
| 660 |
+
# Visibilité conditionnelle
|
| 661 |
+
group_rights, group_remuneration,
|
| 662 |
+
group_work_description, group_image_description,
|
| 663 |
+
group_physical_person, group_legal_entity,
|
| 664 |
+
generate_button_row,
|
| 665 |
+
# Aperçu du contrat
|
| 666 |
+
contract_preview
|
| 667 |
+
]
|
| 668 |
+
)
|
| 669 |
+
|
| 670 |
+
# Mise à jour des affichages conditionnels
|
| 671 |
+
cession_mode.change(
|
| 672 |
+
fn=update_cession_mode_display,
|
| 673 |
+
inputs=[cession_mode],
|
| 674 |
+
outputs=[group_rights]
|
| 675 |
+
)
|
| 676 |
+
|
| 677 |
+
author_type.change(
|
| 678 |
+
fn=update_author_type_display,
|
| 679 |
+
inputs=[author_type],
|
| 680 |
+
outputs=[group_physical_person, group_legal_entity]
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
# Génération du PDF
|
| 684 |
+
generate_button.click(
|
| 685 |
+
fn=generate_pdf,
|
| 686 |
+
inputs=[contract_data, contract_name],
|
| 687 |
+
outputs=[
|
| 688 |
+
generation_status_group, generation_status,
|
| 689 |
+
generation_progress, download_group, pdf_output
|
| 690 |
+
]
|
| 691 |
+
)
|
| 692 |
+
|
| 693 |
+
return demo
|
text_analyzer.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Module d'analyse de texte pour détecter automatiquement le type de contrat nécessaire.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
def analyze_work_description(description):
|
| 6 |
+
"""
|
| 7 |
+
Analyse la description fournie par l'utilisateur pour déterminer le type de contrat approprié.
|
| 8 |
+
|
| 9 |
+
Args:
|
| 10 |
+
description (str): Description fournie par l'utilisateur
|
| 11 |
+
|
| 12 |
+
Returns:
|
| 13 |
+
list: Liste des types de contrats nécessaires ["Auteur (droits d'auteur)", "Image (droit à l'image)"]
|
| 14 |
+
"""
|
| 15 |
+
# Normaliser le texte pour l'analyse
|
| 16 |
+
texte = description.lower()
|
| 17 |
+
|
| 18 |
+
# Initialiser les scores
|
| 19 |
+
score_auteur = 0
|
| 20 |
+
score_image = 0
|
| 21 |
+
|
| 22 |
+
# DROITS D'AUTEUR - Mots-clés par catégorie
|
| 23 |
+
mots_textuels = ["livre", "roman", "nouvelle", "poème", "article", "essai", "mémoire", "thèse", "scénario",
|
| 24 |
+
"manuscrit", "rédaction", "texte", "écriture", "publication", "écrit", "éditorial", "blog",
|
| 25 |
+
"ouvrage", "brochure", "journal", "magazine", "recueil", "rédactionnel"]
|
| 26 |
+
|
| 27 |
+
mots_musicaux = ["musique", "composition", "partition", "chanson", "mélodie", "arrangement", "œuvre musicale",
|
| 28 |
+
"symphonie", "album", "single", "son", "enregistrement", "morceau", "opus", "instrumental",
|
| 29 |
+
"concert", "orchestration", "chant", "lyrics", "paroles", "refrain", "harmonie", "acoustique",
|
| 30 |
+
"piano", "guitare", "violon", "batterie", "chanter", "interprétation musicale"]
|
| 31 |
+
|
| 32 |
+
mots_graphiques = ["peinture", "dessin", "illustration", "graphisme", "logo", "infographie", "design",
|
| 33 |
+
"tableau", "esquisse", "croquis", "fresque", "œuvre plastique", "affiche", "aquarelle",
|
| 34 |
+
"sculpture", "gravure", "sérigraphie", "street art", "graffiti", "photographie artistique",
|
| 35 |
+
"collage", "estampe", "lithographie", "pochette", "couverture"]
|
| 36 |
+
|
| 37 |
+
mots_numeriques = ["logiciel", "site web", "application", "code", "programme", "script", "algorithme",
|
| 38 |
+
"jeu vidéo", "développement", "appli", "interface", "plateforme", "solution digitale",
|
| 39 |
+
"nft", "token", "crypto", "blockchain", "web3", "intelligence artificielle", "ia",
|
| 40 |
+
"données", "base de données", "système", "api", "plugin", "extension", "widget"]
|
| 41 |
+
|
| 42 |
+
mots_architecturaux = ["plan", "maquette", "conception architecturale", "design d'espace", "modélisation 3D",
|
| 43 |
+
"architecture", "structure", "bâtiment", "édifice", "monument", "urbanisme",
|
| 44 |
+
"aménagement", "paysagisme", "intérieur", "déco", "décoration"]
|
| 45 |
+
|
| 46 |
+
mots_autres_auteur = ["concept", "idée", "invention", "savoir-faire", "méthode", "formule", "base de données",
|
| 47 |
+
"compilation", "collection", "œuvre", "création", "synthèse", "travail", "production",
|
| 48 |
+
"droit d'auteur", "propriété intellectuelle", "copyright", "brevet"]
|
| 49 |
+
|
| 50 |
+
# DROITS À L'IMAGE - Mots-clés par catégorie
|
| 51 |
+
mots_image_personne = ["photographie", "portrait", "silhouette", "apparence", "visage", "corps", "identité visuelle",
|
| 52 |
+
"selfie", "avatar", "photo d'identité", "figure", "physionomie", "traits", "mannequin",
|
| 53 |
+
"modèle photo", "acteur", "actrice", "comédien", "comédienne", "figurant", "témoignage vidéo"]
|
| 54 |
+
|
| 55 |
+
mots_image_contexte = ["pose", "séance photo", "shooting", "mannequin", "modèle", "figurant", "mise en scène",
|
| 56 |
+
"studio photo", "apparition", "photoshoot", "objectif", "capturer", "appareil photo",
|
| 57 |
+
"caméra", "filmer", "enregistrer", "photographier", "filmer", "enregistrer"]
|
| 58 |
+
|
| 59 |
+
mots_image_visibilité = ["identifiable", "reconnaissable", "apparaître", "figurer", "posant", "visible",
|
| 60 |
+
"présent", "participation", "exposé", "exhibé", "montré", "droit à l'image",
|
| 61 |
+
"consentement", "image", "vidéo"]
|
| 62 |
+
|
| 63 |
+
mots_image_supports = ["photo", "image", "pellicule", "cliché", "instantané", "polaroid", "négatif",
|
| 64 |
+
"diapositive", "tirage", "impression photographique", "portrait", "photomaton"]
|
| 65 |
+
|
| 66 |
+
# COMBINAISON DROITS D'AUTEUR + DROITS À L'IMAGE
|
| 67 |
+
mots_videos = ["vidéo", "film", "court-métrage", "clip", "documentaire", "reportage", "captation", "tournage",
|
| 68 |
+
"filmé", "filmage", "enregistrement vidéo", "séquence", "rushes", "montage vidéo", "movie",
|
| 69 |
+
"cinéma", "réalisation", "vidéaste", "youtubeur", "youtubeuse", "influenceur", "influenceuse",
|
| 70 |
+
"vlog", "tiktok", "instagram", "content creator", "créateur de contenu"]
|
| 71 |
+
|
| 72 |
+
mots_performances = ["performance", "spectacle", "concert", "prestation", "apparition", "interprétation",
|
| 73 |
+
"récital", "show", "émission", "interview", "représentation", "apparition publique",
|
| 74 |
+
"scène", "plateaux", "théâtre", "danse", "chorégraphie", "ballet", "opéra", "music-hall",
|
| 75 |
+
"one man show", "stand-up", "humoriste", "conférence", "conférencier"]
|
| 76 |
+
|
| 77 |
+
mots_digital_personnel = ["stream", "livestream", "webinaire", "podcast vidéo", "tutoriel vidéo", "cours filmé",
|
| 78 |
+
"formation vidéo", "diffusion en direct", "chaîne youtube", "vlog", "direct",
|
| 79 |
+
"twitch", "live", "enregistrement zoom", "vidéoconférence", "stories", "reels"]
|
| 80 |
+
|
| 81 |
+
mots_œuvres_mixtes = ["œuvre audiovisuelle", "multimédia", "installation artistique interactive",
|
| 82 |
+
"réalité virtuelle", "réalité augmentée", "performance audiovisuelle", "mapping vidéo",
|
| 83 |
+
"projection", "hologramme", "expo interactive", "jeu immersif"]
|
| 84 |
+
|
| 85 |
+
# Vérifier les correspondances pour les droits d'auteur
|
| 86 |
+
for mot in mots_textuels + mots_musicaux + mots_graphiques + mots_numeriques + mots_architecturaux + mots_autres_auteur:
|
| 87 |
+
if mot in texte or any(m in texte for m in mot.split()):
|
| 88 |
+
score_auteur += 1
|
| 89 |
+
|
| 90 |
+
# Vérifier les correspondances pour les droits à l'image
|
| 91 |
+
for mot in mots_image_personne + mots_image_contexte + mots_image_visibilité + mots_image_supports:
|
| 92 |
+
if mot in texte or any(m in texte for m in mot.split()):
|
| 93 |
+
score_image += 1
|
| 94 |
+
|
| 95 |
+
# Vérifier les correspondances pour la combinaison (augmente les deux scores)
|
| 96 |
+
for mot in mots_videos + mots_performances + mots_digital_personnel + mots_œuvres_mixtes:
|
| 97 |
+
if mot in texte or any(m in texte for m in mot.split()):
|
| 98 |
+
score_auteur += 1
|
| 99 |
+
score_image += 1
|
| 100 |
+
|
| 101 |
+
# Cas spéciaux nécessitant une analyse plus contextuelle
|
| 102 |
+
if "chant" in texte or "chante" in texte or "chanson" in texte:
|
| 103 |
+
score_auteur += 1
|
| 104 |
+
if any(mot in texte for mot in ["vidéo", "film", "enregistrement vidéo", "youtube", "clip"]):
|
| 105 |
+
score_image += 1
|
| 106 |
+
|
| 107 |
+
if "exposit" in texte: # exposition/expositions
|
| 108 |
+
score_auteur += 1
|
| 109 |
+
if any(mot in texte for mot in ["photo", "portrait", "modèle", "personne"]):
|
| 110 |
+
score_image += 1
|
| 111 |
+
|
| 112 |
+
# Analyser les combinaisons courantes
|
| 113 |
+
if "voix" in texte:
|
| 114 |
+
score_auteur += 1
|
| 115 |
+
if "visage" in texte or "image" in texte or "vidéo" in texte:
|
| 116 |
+
score_image += 1
|
| 117 |
+
|
| 118 |
+
# Déterminer le type de contrat
|
| 119 |
+
types_contrat = []
|
| 120 |
+
if score_auteur > 0:
|
| 121 |
+
types_contrat.append("Auteur (droits d'auteur)")
|
| 122 |
+
if score_image > 0:
|
| 123 |
+
types_contrat.append("Image (droit à l'image)")
|
| 124 |
+
|
| 125 |
+
# Si aucun type détecté, suggérer les deux par sécurité
|
| 126 |
+
if not types_contrat:
|
| 127 |
+
return ["Auteur (droits d'auteur)", "Image (droit à l'image)"]
|
| 128 |
+
|
| 129 |
+
return types_contrat
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def get_explanation(detected_types):
|
| 133 |
+
"""
|
| 134 |
+
Génère une explication claire des types de contrats détectés.
|
| 135 |
+
|
| 136 |
+
Args:
|
| 137 |
+
detected_types (list): Types de contrats détectés
|
| 138 |
+
|
| 139 |
+
Returns:
|
| 140 |
+
str: Explication pour l'utilisateur
|
| 141 |
+
"""
|
| 142 |
+
if len(detected_types) == 2:
|
| 143 |
+
return """
|
| 144 |
+
J'ai détecté que vous avez besoin d'un **contrat combiné de cession de droits d'auteur et de droits à l'image**.
|
| 145 |
+
|
| 146 |
+
Ce type de contrat est adapté lorsque:
|
| 147 |
+
- L'œuvre est protégée par le droit d'auteur (texte, musique, design, etc.)
|
| 148 |
+
- ET l'image d'une personne est visible et exploitée (vidéo, photo, etc.)
|
| 149 |
+
|
| 150 |
+
Vous pouvez modifier cette sélection si nécessaire.
|
| 151 |
+
"""
|
| 152 |
+
|
| 153 |
+
elif "Auteur (droits d'auteur)" in detected_types:
|
| 154 |
+
return """
|
| 155 |
+
J'ai détecté que vous avez besoin d'un **contrat de cession de droits d'auteur** uniquement.
|
| 156 |
+
|
| 157 |
+
Ce type de contrat est adapté pour les œuvres protégées comme:
|
| 158 |
+
- Textes, livres, articles
|
| 159 |
+
- Musiques, compositions
|
| 160 |
+
- Dessins, peintures, designs
|
| 161 |
+
- Logiciels, sites web
|
| 162 |
+
- Et autres créations originales
|
| 163 |
+
|
| 164 |
+
Si votre projet implique également l'image reconnaissable d'une personne, vous pourriez aussi avoir besoin d'un contrat de droits à l'image.
|
| 165 |
+
"""
|
| 166 |
+
|
| 167 |
+
elif "Image (droit à l'image)" in detected_types:
|
| 168 |
+
return """
|
| 169 |
+
J'ai détecté que vous avez besoin d'un **contrat de cession de droits à l'image** uniquement.
|
| 170 |
+
|
| 171 |
+
Ce type de contrat est adapté lorsque:
|
| 172 |
+
- L'image ou la vidéo d'une personne est utilisée
|
| 173 |
+
- La personne est identifiable ou reconnaissable
|
| 174 |
+
- Il n'y a pas d'œuvre originale protégée par le droit d'auteur
|
| 175 |
+
|
| 176 |
+
Si votre projet implique également une œuvre originale, vous pourriez aussi avoir besoin d'un contrat de cession de droits d'auteur.
|
| 177 |
+
"""
|
| 178 |
+
|
| 179 |
+
else:
|
| 180 |
+
return """
|
| 181 |
+
Je n'ai pas pu déterminer automatiquement le type de contrat nécessaire.
|
| 182 |
+
|
| 183 |
+
Veuillez sélectionner manuellement:
|
| 184 |
+
- Contrat de droits d'auteur: pour les œuvres originales (textes, musiques, designs, etc.)
|
| 185 |
+
- Contrat de droits à l'image: pour l'utilisation de l'image d'une personne
|
| 186 |
+
- Les deux: si les deux aspects sont présents
|
| 187 |
+
"""
|
wizard_style.css
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Style personnalisé pour l'interface du générateur de contrats en mode wizard */
|
| 2 |
+
|
| 3 |
+
/* Style global */
|
| 4 |
+
body {
|
| 5 |
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
/* Style pour le conteneur principal */
|
| 9 |
+
.gradio-container {
|
| 10 |
+
max-width: 1400px;
|
| 11 |
+
margin: 0 auto;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
/* Style pour la barre de progression */
|
| 15 |
+
.progress-container {
|
| 16 |
+
margin-bottom: 20px;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
/* Style pour l'aperçu du contrat */
|
| 20 |
+
#contract-preview {
|
| 21 |
+
max-height: 700px;
|
| 22 |
+
overflow-y: auto;
|
| 23 |
+
padding: 25px;
|
| 24 |
+
border: 1px solid #ddd;
|
| 25 |
+
border-radius: 8px;
|
| 26 |
+
background-color: #f9f9f9;
|
| 27 |
+
font-family: 'Times New Roman', Times, serif;
|
| 28 |
+
line-height: 1.6;
|
| 29 |
+
font-size: 14px;
|
| 30 |
+
margin-top: 10px;
|
| 31 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/* Style pour la suggestion de type de contrat */
|
| 35 |
+
#contract-suggestion {
|
| 36 |
+
padding: 15px;
|
| 37 |
+
border-left: 4px solid #3b82f6;
|
| 38 |
+
background-color: #f0f7ff;
|
| 39 |
+
margin: 15px 0;
|
| 40 |
+
border-radius: 0 4px 4px 0;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
/* Style pour les titres dans la prévisualisation */
|
| 44 |
+
#contract-preview h3 {
|
| 45 |
+
color: #222;
|
| 46 |
+
font-weight: bold;
|
| 47 |
+
margin-top: 15px;
|
| 48 |
+
margin-bottom: 5px;
|
| 49 |
+
border-bottom: 1px solid #ccc;
|
| 50 |
+
padding-bottom: 5px;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
/* Style pour mettre en évidence les saisies utilisateur */
|
| 54 |
+
#contract-preview strong {
|
| 55 |
+
color: #0056b3;
|
| 56 |
+
font-weight: bold;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
/* Style pour le bouton de génération */
|
| 60 |
+
#generate-btn {
|
| 61 |
+
background-color: #2563eb;
|
| 62 |
+
color: white;
|
| 63 |
+
font-weight: bold;
|
| 64 |
+
padding: 12px 24px;
|
| 65 |
+
border-radius: 5px;
|
| 66 |
+
transition: all 0.3s;
|
| 67 |
+
font-size: 16px;
|
| 68 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
#generate-btn:hover {
|
| 72 |
+
background-color: #1d4ed8;
|
| 73 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
| 74 |
+
transform: translateY(-2px);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/* Style pour les boutons de navigation */
|
| 78 |
+
.navigation-buttons {
|
| 79 |
+
margin-top: 20px;
|
| 80 |
+
display: flex;
|
| 81 |
+
justify-content: space-between;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
/* Style pour les titres de section */
|
| 85 |
+
.gradio-container h2 {
|
| 86 |
+
color: #1e40af;
|
| 87 |
+
border-bottom: 2px solid #e5e7eb;
|
| 88 |
+
padding-bottom: 10px;
|
| 89 |
+
margin-top: 30px;
|
| 90 |
+
margin-bottom: 20px;
|
| 91 |
+
font-size: 24px;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.gradio-container h3 {
|
| 95 |
+
color: #333;
|
| 96 |
+
margin-top: 20px;
|
| 97 |
+
font-size: 18px;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
/* Style pour les groupes */
|
| 101 |
+
.gradio-container .gr-group {
|
| 102 |
+
border: 1px solid #e5e7eb;
|
| 103 |
+
border-radius: 8px;
|
| 104 |
+
padding: 15px;
|
| 105 |
+
margin-bottom: 20px;
|
| 106 |
+
background-color: #fff;
|
| 107 |
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
/* Style pour les labels des champs */
|
| 111 |
+
.gradio-container label {
|
| 112 |
+
font-weight: 600;
|
| 113 |
+
color: #4b5563;
|
| 114 |
+
margin-bottom: 5px;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
/* Style pour les champs du formulaire */
|
| 118 |
+
.gradio-container input[type="text"],
|
| 119 |
+
.gradio-container textarea {
|
| 120 |
+
border: 1px solid #d1d5db;
|
| 121 |
+
border-radius: 6px;
|
| 122 |
+
padding: 10px 14px;
|
| 123 |
+
transition: all 0.3s;
|
| 124 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
.gradio-container input[type="text"]:focus,
|
| 128 |
+
.gradio-container textarea:focus {
|
| 129 |
+
border-color: #3b82f6;
|
| 130 |
+
outline: none;
|
| 131 |
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
/* Style pour les informations/descriptions */
|
| 135 |
+
.gradio-container .info {
|
| 136 |
+
color: #6b7280;
|
| 137 |
+
font-size: 14px;
|
| 138 |
+
margin-top: 4px;
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
/* Style pour les étapes du wizard */
|
| 142 |
+
.step-number {
|
| 143 |
+
display: inline-block;
|
| 144 |
+
width: 30px;
|
| 145 |
+
height: 30px;
|
| 146 |
+
background-color: #3b82f6;
|
| 147 |
+
color: white;
|
| 148 |
+
border-radius: 50%;
|
| 149 |
+
text-align: center;
|
| 150 |
+
line-height: 30px;
|
| 151 |
+
margin-right: 10px;
|
| 152 |
+
font-weight: bold;
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
/* Style pour les informations d'aide */
|
| 156 |
+
.help-text {
|
| 157 |
+
background-color: #f3f4f6;
|
| 158 |
+
padding: 12px;
|
| 159 |
+
border-radius: 6px;
|
| 160 |
+
margin-bottom: 15px;
|
| 161 |
+
border-left: 4px solid #9ca3af;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
/* Style pour l'indicateur de progression */
|
| 165 |
+
.progress-indicator {
|
| 166 |
+
display: flex;
|
| 167 |
+
justify-content: space-between;
|
| 168 |
+
align-items: center;
|
| 169 |
+
margin-bottom: 30px;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
.progress-step {
|
| 173 |
+
flex: 1;
|
| 174 |
+
text-align: center;
|
| 175 |
+
position: relative;
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
.progress-step:not(:last-child):after {
|
| 179 |
+
content: '';
|
| 180 |
+
position: absolute;
|
| 181 |
+
top: 50%;
|
| 182 |
+
right: 0;
|
| 183 |
+
width: 100%;
|
| 184 |
+
height: 2px;
|
| 185 |
+
background-color: #e5e7eb;
|
| 186 |
+
z-index: 1;
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
.progress-step.active:not(:last-child):after,
|
| 190 |
+
.progress-step.completed:not(:last-child):after {
|
| 191 |
+
background-color: #3b82f6;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
.progress-step-circle {
|
| 195 |
+
width: 24px;
|
| 196 |
+
height: 24px;
|
| 197 |
+
border-radius: 50%;
|
| 198 |
+
background-color: #e5e7eb;
|
| 199 |
+
display: inline-flex;
|
| 200 |
+
justify-content: center;
|
| 201 |
+
align-items: center;
|
| 202 |
+
position: relative;
|
| 203 |
+
z-index: 2;
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
.progress-step.active .progress-step-circle,
|
| 207 |
+
.progress-step.completed .progress-step-circle {
|
| 208 |
+
background-color: #3b82f6;
|
| 209 |
+
color: white;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
.progress-step-text {
|
| 213 |
+
font-size: 12px;
|
| 214 |
+
margin-top: 8px;
|
| 215 |
+
color: #6b7280;
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
.progress-step.active .progress-step-text,
|
| 219 |
+
.progress-step.completed .progress-step-text {
|
| 220 |
+
color: #1e40af;
|
| 221 |
+
font-weight: 600;
|
| 222 |
+
}
|