Public Access
Amélioration contraste mobile, traçabilité des IA et filtrage catalogue
Build and Push Docker Image / build-and-push (push) Successful in 35s
Build and Push Docker Image / build-and-push (push) Successful in 35s
This commit is contained in:
1 parent
3e140d7d6f
commit
9fdb2bd73e
7 files changed
+114
-18
No files matched your search
@@ -11824,5 +11824,41 @@
|
|||||||
"action": "Acc\u00e8s \u00e0 /",
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
"date": "2026-08-23 11:36:33",
|
"date": "2026-08-23 11:36:33",
|
||||||
"timestamp": 1787477793.7782843
|
"timestamp": 1787477793.7782843
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
|
"date": "2026-08-23 14:50:01",
|
||||||
|
"timestamp": 1787489401.7572532
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /chat",
|
||||||
|
"date": "2026-08-23 14:53:10",
|
||||||
|
"timestamp": 1787489590.9684572
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
|
"date": "2026-08-23 15:04:55",
|
||||||
|
"timestamp": 1787490295.170784
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
|
"date": "2026-08-23 15:05:06",
|
||||||
|
"timestamp": 1787490306.0473568
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
|
"date": "2026-08-23 15:05:37",
|
||||||
|
"timestamp": 1787490337.0444038
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ip": "127.0.0.1",
|
||||||
|
"action": "Acc\u00e8s \u00e0 /",
|
||||||
|
"date": "2026-08-23 15:08:53",
|
||||||
|
"timestamp": 1787490533.299349
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -37,11 +37,12 @@ def executer_synthese_multi_ia(prompt_securise: str, fournisseur: str, mode_paya
|
|||||||
|
|
||||||
def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False, multi_ia: bool = False, chat_id: str = None):
|
def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False, multi_ia: bool = False, chat_id: str = None):
|
||||||
prompt_securise = f"{SYSTEM_CONFIDENTIALITY_INSTRUCTION}\n\nRequête utilisateur :\n{prompt}"
|
prompt_securise = f"{SYSTEM_CONFIDENTIALITY_INSTRUCTION}\n\nRequête utilisateur :\n{prompt}"
|
||||||
|
trace_tentatives = []
|
||||||
|
|
||||||
if multi_ia or fournisseur in ["openrouter_code", "openrouter_droit"]:
|
if multi_ia or fournisseur in ["openrouter_code", "openrouter_droit"]:
|
||||||
resultat_multi = executer_synthese_multi_ia(prompt_securise, fournisseur, mode_payant, chat_id)
|
resultat_multi = executer_synthese_multi_ia(prompt_securise, fournisseur, mode_payant, chat_id)
|
||||||
if resultat_multi:
|
if resultat_multi:
|
||||||
return resultat_multi
|
return resultat_multi[0], resultat_multi[1], resultat_multi[2], resultat_multi[3], ["🟢 Multi-IA / Synthèse Gemini réussie"]
|
||||||
|
|
||||||
if fournisseur == "gemini":
|
if fournisseur == "gemini":
|
||||||
ordre_repli = [interroger_gemini, interroger_openrouter, interroger_local_ollama]
|
ordre_repli = [interroger_gemini, interroger_openrouter, interroger_local_ollama]
|
||||||
@@ -51,6 +52,7 @@ def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False
|
|||||||
ordre_repli = [interroger_local_ollama, interroger_openrouter, interroger_gemini]
|
ordre_repli = [interroger_local_ollama, interroger_openrouter, interroger_gemini]
|
||||||
|
|
||||||
for fonction_api in ordre_repli:
|
for fonction_api in ordre_repli:
|
||||||
|
nom_tente = fonction_api.__name__.replace("interroger_", "")
|
||||||
try:
|
try:
|
||||||
if fonction_api.__name__ == "interroger_openrouter":
|
if fonction_api.__name__ == "interroger_openrouter":
|
||||||
texte, couleur, nom_fournisseur, nom_modele, _, _ = fonction_api(
|
texte, couleur, nom_fournisseur, nom_modele, _, _ = fonction_api(
|
||||||
@@ -60,9 +62,12 @@ def executer_requete_ia(fournisseur: str, prompt: str, mode_payant: bool = False
|
|||||||
texte, couleur, nom_fournisseur, nom_modele = fonction_api(prompt_securise)
|
texte, couleur, nom_fournisseur, nom_modele = fonction_api(prompt_securise)
|
||||||
|
|
||||||
if texte:
|
if texte:
|
||||||
return texte, couleur, nom_fournisseur, nom_modele
|
trace_tentatives.append(f"🟢 {nom_tente.upper()} ({nom_modele}) : Succès")
|
||||||
|
return texte, couleur, nom_fournisseur, nom_modele, trace_tentatives
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
msg_err = str(e).split("-")[0].strip()[:50] # On raccourcit l'erreur pour l'affichage
|
||||||
|
trace_tentatives.append(f"🔴 {nom_tente.upper()} : Échec ({msg_err})")
|
||||||
log_event("WARNING", "ORCHESTRATEUR", f"Basculement suite à l'échec de {fonction_api.__name__} : {e}")
|
log_event("WARNING", "ORCHESTRATEUR", f"Basculement suite à l'échec de {fonction_api.__name__} : {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raise Exception("Tous les fournisseurs d'IA sont indisponibles.")
|
raise Exception(f"Tous les fournisseurs d'IA ont échoué. Trace : {' | '.join(trace_tentatives)}")
|
||||||
+13
-2
@@ -138,7 +138,9 @@ async def handle_chat_request(request, session):
|
|||||||
if prix_estime > obtenir_solde(utilisateur_nom):
|
if prix_estime > obtenir_solde(utilisateur_nom):
|
||||||
return Div(H3("❌ Solde Insuffisant", style="color:red;"), P("Rechargez votre compte."), style="background:#300; padding:15px;")
|
return Div(H3("❌ Solde Insuffisant", style="color:red;"), P("Rechargez votre compte."), style="background:#300; padding:15px;")
|
||||||
|
|
||||||
reponse, couleur, fournisseur_nom, modele = executer_requete_ia(fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia, chat_id=chat_id)
|
reponse, couleur, fournisseur_nom, modele, trace_route = executer_requete_ia(
|
||||||
|
fournisseur, full_prompt, mode_payant=mode_payant, multi_ia=mode_multi_ia, chat_id=chat_id
|
||||||
|
)
|
||||||
|
|
||||||
if "openrouter" in fournisseur_nom.lower() and mode_payant:
|
if "openrouter" in fournisseur_nom.lower() and mode_payant:
|
||||||
verifier_et_valider_budget(full_prompt, modele, utilisateur_nom=utilisateur_nom, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
verifier_et_valider_budget(full_prompt, modele, utilisateur_nom=utilisateur_nom, rang_utilisateur=role_utilisateur, multi_ia=mode_multi_ia)
|
||||||
@@ -154,7 +156,16 @@ async def handle_chat_request(request, session):
|
|||||||
if file_names: display_msg += f"\n\n📎 **Fichiers joints :** {', '.join(file_names)}"
|
if file_names: display_msg += f"\n\n📎 **Fichiers joints :** {', '.join(file_names)}"
|
||||||
|
|
||||||
history[chat_id]["messages"].append({"role": "user", "content": display_msg})
|
history[chat_id]["messages"].append({"role": "user", "content": display_msg})
|
||||||
history[chat_id]["messages"].append({"role": "ai", "content": reponse, "intention": intention, "fournisseur": fournisseur_nom, "modele": modele, "couleur": couleur, "execution_time": execution_time})
|
history[chat_id]["messages"].append({
|
||||||
|
"role": "ai",
|
||||||
|
"content": reponse,
|
||||||
|
"intention": intention,
|
||||||
|
"fournisseur": fournisseur_nom,
|
||||||
|
"modele": modele,
|
||||||
|
"couleur": couleur,
|
||||||
|
"execution_time": execution_time,
|
||||||
|
"trace": trace_route # <--- Ajout de la traçabilité
|
||||||
|
})
|
||||||
save_history(history)
|
save_history(history)
|
||||||
|
|
||||||
return render_user_msg(display_msg), render_ai_msg(reponse, intention, fournisseur_nom, modele, couleur, execution_time), Script("renderMarkdown();"), render_sidebar()
|
return render_user_msg(display_msg), render_ai_msg(reponse, intention, fournisseur_nom, modele, couleur, execution_time), Script("renderMarkdown();"), render_sidebar()
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ def _do_update():
|
|||||||
pricing = m.get("pricing", {})
|
pricing = m.get("pricing", {})
|
||||||
# Gratuits purs
|
# Gratuits purs
|
||||||
if pricing.get("prompt") == "0" and pricing.get("completion") == "0":
|
if pricing.get("prompt") == "0" and pricing.get("completion") == "0":
|
||||||
|
if "nvidia/nemotron" not in m["id"].lower():
|
||||||
modeles_gratuits.append(m["id"])
|
modeles_gratuits.append(m["id"])
|
||||||
# Ciblage des payants ultra-performants (Claude & OpenAI)
|
# Ciblage des payants ultra-performants (Claude & OpenAI)
|
||||||
elif "claude-3.5-sonnet" in m["id"].lower() or "gpt-4o" in m["id"].lower():
|
elif "claude-3.5-sonnet" in m["id"].lower() or "gpt-4o" in m["id"].lower():
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
def nettoyer_decision_router(raw_text):
|
||||||
|
try:
|
||||||
|
clean = re.sub(r'```(?:json)?\s*', '', raw_text)
|
||||||
|
clean = clean.replace('```', '').strip()
|
||||||
|
start = clean.find('{')
|
||||||
|
end = clean.rfind('}') + 1
|
||||||
|
if start != -1 and end != 0:
|
||||||
|
return json.loads(clean[start:end])
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return {"intention": "Analyse", "fournisseur": "gemini"}
|
||||||
+10
-8
@@ -11,7 +11,7 @@ def get_theme_hdrs():
|
|||||||
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"),
|
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"),
|
||||||
Link(rel="icon", type="image/svg+xml", href=f"data:image/svg+xml;utf8,{svg_favicon}"),
|
Link(rel="icon", type="image/svg+xml", href=f"data:image/svg+xml;utf8,{svg_favicon}"),
|
||||||
Style("""
|
Style("""
|
||||||
:root { --bg-dark: #121212; --bg-panel: #1e1e24; --accent-blue: #00e5ff; --accent-purple: #b300ff; --text-main: #e0e0e0; }
|
:root { --bg-dark: #121212; --bg-panel: #1e1e24; --accent-blue: #00e5ff; --accent-purple: #b300ff; --text-main: #f8fafc; }
|
||||||
body { background-color: var(--bg-dark); color: var(--text-main); font-family: 'Segoe UI', Tahoma, sans-serif; margin: 0; display: flex; height: 100vh; overflow: hidden; box-sizing: border-box; }
|
body { background-color: var(--bg-dark); color: var(--text-main); font-family: 'Segoe UI', Tahoma, sans-serif; margin: 0; display: flex; height: 100vh; overflow: hidden; box-sizing: border-box; }
|
||||||
|
|
||||||
/* --- SIDEBAR CLASSIQUE --- */
|
/* --- SIDEBAR CLASSIQUE --- */
|
||||||
@@ -30,13 +30,17 @@ def get_theme_hdrs():
|
|||||||
.history-btn { background: none; border: none; color: #888; cursor: pointer; padding: 2px 5px; font-size: 1.1em; }
|
.history-btn { background: none; border: none; color: #888; cursor: pointer; padding: 2px 5px; font-size: 1.1em; }
|
||||||
.history-btn:hover { color: var(--accent-blue); }
|
.history-btn:hover { color: var(--accent-blue); }
|
||||||
|
|
||||||
/* --- CONTENU CENTRAL --- */
|
/* --- CONTENU CENTRAL & CONTRASTE MOBILE ACCRU --- */
|
||||||
.main-content { flex: 1; display: flex; flex-direction: column; height: 100vh; position: relative; box-sizing: border-box; }
|
.main-content { flex: 1; display: flex; flex-direction: column; height: 100vh; position: relative; box-sizing: border-box; }
|
||||||
.chat-container { flex: 1; padding: 20px; max-width: 1000px; margin: 0 auto; width: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; padding-bottom: 160px; box-sizing: border-box; }
|
.chat-container { flex: 1; padding: 20px; max-width: 1000px; margin: 0 auto; width: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; padding-bottom: 160px; box-sizing: border-box; }
|
||||||
.user-bubble { background-color: #2a2a35; padding: 15px 20px; border-radius: 15px 15px 0 15px; align-self: flex-end; max-width: 80%; border: 1px solid #444; margin-left: auto; word-break: break-word; overflow-wrap: break-word; }
|
.user-bubble { background-color: #2a2a35; color: #ffffff; padding: 15px 20px; border-radius: 15px 15px 0 15px; align-self: flex-end; max-width: 80%; border: 1px solid #555; margin-left: auto; word-break: break-word; overflow-wrap: break-word; }
|
||||||
.ai-bubble { background-color: var(--bg-panel); padding: 15px 20px; border-radius: 15px 15px 15px 0; align-self: flex-start; max-width: 90%; width: 100%; border: 1px solid #333; word-break: break-word; overflow-wrap: break-word; }
|
|
||||||
.msg-actions { display: flex; gap: 10px; margin-top: 15px; border-top: 1px dashed #333; padding-top: 10px; justify-content: flex-end; flex-wrap: wrap; }
|
/* BULLE IA : Contraste maximal forcé pour le confort de lecture mobile */
|
||||||
.msg-action-btn { background: transparent; border: 1px solid #444; color: #888; border-radius: 15px; padding: 5px 12px; cursor: pointer; font-size: 0.85em; transition: 0.2s; }
|
.ai-bubble { background-color: var(--bg-panel); color: #ffffff !important; padding: 15px 20px; border-radius: 15px 15px 15px 0; align-self: flex-start; max-width: 90%; width: 100%; border: 1px solid #444; word-break: break-word; overflow-wrap: break-word; }
|
||||||
|
.markdown-rendered, .markdown-rendered p, .markdown-rendered li, .markdown-rendered span, .markdown-rendered strong { color: #ffffff !important; }
|
||||||
|
|
||||||
|
.msg-actions { display: flex; gap: 10px; margin-top: 15px; border-top: 1px dashed #444; padding-top: 10px; justify-content: flex-end; flex-wrap: wrap; }
|
||||||
|
.msg-action-btn { background: transparent; border: 1px solid #555; color: #ccc; border-radius: 15px; padding: 5px 12px; cursor: pointer; font-size: 0.85em; transition: 0.2s; }
|
||||||
.msg-action-btn:hover { background: #2a2a35; color: var(--accent-blue); border-color: var(--accent-blue); }
|
.msg-action-btn:hover { background: #2a2a35; color: var(--accent-blue); border-color: var(--accent-blue); }
|
||||||
.code-header { background: #16161a; padding: 8px 15px; display: flex; justify-content: space-between; align-items: center; border-radius: 8px 8px 0 0; font-family: monospace; font-size: 0.85em; border: 1px solid #333; border-bottom: none; }
|
.code-header { background: #16161a; padding: 8px 15px; display: flex; justify-content: space-between; align-items: center; border-radius: 8px 8px 0 0; font-family: monospace; font-size: 0.85em; border: 1px solid #333; border-bottom: none; }
|
||||||
.code-actions-group { display: flex; gap: 8px; }
|
.code-actions-group { display: flex; gap: 8px; }
|
||||||
@@ -85,7 +89,6 @@ def get_theme_hdrs():
|
|||||||
Script(r"""
|
Script(r"""
|
||||||
let aethasFiles = new DataTransfer();
|
let aethasFiles = new DataTransfer();
|
||||||
|
|
||||||
// JS pour le bouton Hamburger
|
|
||||||
window.toggleSidebar = function() {
|
window.toggleSidebar = function() {
|
||||||
let sidebar = document.querySelector('.sidebar');
|
let sidebar = document.querySelector('.sidebar');
|
||||||
if (sidebar) {
|
if (sidebar) {
|
||||||
@@ -250,7 +253,6 @@ def get_theme_hdrs():
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ... (Fonctions de téléchargement ZIP, PDF, DOC, TXT, JSON et Partage identiques et préservées)
|
|
||||||
function downloadCodeZip(btn, lang) {
|
function downloadCodeZip(btn, lang) {
|
||||||
let pre = btn.closest('.code-header').nextElementSibling;
|
let pre = btn.closest('.code-header').nextElementSibling;
|
||||||
let code = pre.querySelector('code').innerText;
|
let code = pre.querySelector('code').innerText;
|
||||||
|
|||||||
@@ -17,14 +17,22 @@ def get_openrouter_stats():
|
|||||||
def render_user_msg(text):
|
def render_user_msg(text):
|
||||||
return Div(P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"), cls="user-bubble")
|
return Div(P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"), cls="user-bubble")
|
||||||
|
|
||||||
def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, execution_time=None):
|
def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, execution_time=None, trace=None):
|
||||||
time_badge = Span(f"⏱️ {execution_time}s", style="margin-right: 10px; color:#aaa; font-size:0.8em;") if execution_time else ""
|
time_badge = Span(f"⏱️ {execution_time}s", style="margin-right: 10px; color:#aaa; font-size:0.8em;") if execution_time else ""
|
||||||
|
|
||||||
|
# Construction de la trace visuelle si elle existe
|
||||||
|
trace_html = ""
|
||||||
|
if trace and isinstance(trace, list):
|
||||||
|
badges = [Span(t, style="font-size:0.75em; background:#222; padding:2px 6px; border-radius:4px; border:1px solid #444;") for t in trace]
|
||||||
|
trace_html = Div(Span("Route : ", style="color:#888; font-size:0.75em;"), *badges, style="display:flex; gap:5px; align-items:center; margin-bottom:8px; flex-wrap:wrap;")
|
||||||
|
|
||||||
encart_info = Div(
|
encart_info = Div(
|
||||||
Span(f"🎯 {intention}", style="margin-right: 10px; color:#aaa; font-size:0.8em;"),
|
Span(f"🎯 {intention}", style="margin-right: 10px; color:#aaa; font-size:0.8em;"),
|
||||||
Span(f"🏢 {nom_fournisseur}", style="margin-right: 10px; color:#aaa; font-size:0.8em;"),
|
Span(f"🏢 {nom_fournisseur}", style="margin-right: 10px; color:#aaa; font-size:0.8em;"),
|
||||||
Span(f"🧠 {nom_modele}", style=f"margin-right: 10px; color:{couleur}; font-weight:bold; font-size:0.8em;"),
|
Span(f"🧠 {nom_modele}", style=f"margin-right: 10px; color:{couleur}; font-weight:bold; font-size:0.8em;"),
|
||||||
time_badge, style="border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 15px; display: flex; flex-wrap: wrap; gap: 5px;"
|
time_badge, style="border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 10px; display: flex; flex-wrap: wrap; gap: 5px;"
|
||||||
)
|
)
|
||||||
|
|
||||||
actions = Div(
|
actions = Div(
|
||||||
Button("📄 PDF", onclick="downloadResponsePDF(this)", cls="msg-action-btn", title="Télécharger en PDF"),
|
Button("📄 PDF", onclick="downloadResponsePDF(this)", cls="msg-action-btn", title="Télécharger en PDF"),
|
||||||
Button("📘 DOC", onclick="downloadResponseDOC(this)", cls="msg-action-btn", title="Télécharger en Word"),
|
Button("📘 DOC", onclick="downloadResponseDOC(this)", cls="msg-action-btn", title="Télécharger en Word"),
|
||||||
@@ -34,7 +42,16 @@ def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur, executi
|
|||||||
Button("📧 Email", onclick="shareEmail(this)", cls="msg-action-btn", title="Envoyer par courriel"),
|
Button("📧 Email", onclick="shareEmail(this)", cls="msg-action-btn", title="Envoyer par courriel"),
|
||||||
cls="msg-actions"
|
cls="msg-actions"
|
||||||
)
|
)
|
||||||
return Div(encart_info, Div(text, cls="markdown-raw", style="display:none;"), Div(cls="markdown-rendered", style="line-height: 1.6;"), actions, cls="ai-bubble", style=f"border-left: 4px solid {couleur};")
|
|
||||||
|
return Div(
|
||||||
|
trace_html, # Affichage du chemin de routage
|
||||||
|
encart_info,
|
||||||
|
Div(text, cls="markdown-raw", style="display:none;"),
|
||||||
|
Div(cls="markdown-rendered", style="line-height: 1.6;"),
|
||||||
|
actions,
|
||||||
|
cls="ai-bubble",
|
||||||
|
style=f"border-left: 4px solid {couleur};"
|
||||||
|
)
|
||||||
|
|
||||||
def render_sidebar():
|
def render_sidebar():
|
||||||
history = load_history()
|
history = load_history()
|
||||||
@@ -50,8 +67,18 @@ def render_main_page(chat_id, utilisateur_nom="xavier", role="Super Admin"):
|
|||||||
messages_ui = []
|
messages_ui = []
|
||||||
if chat_id in history:
|
if chat_id in history:
|
||||||
for msg in history[chat_id].get('messages', []):
|
for msg in history[chat_id].get('messages', []):
|
||||||
if msg['role'] == 'user': messages_ui.append(render_user_msg(msg['content']))
|
if msg['role'] == 'user':
|
||||||
else: messages_ui.append(render_ai_msg(msg['content'], msg.get('intention', ''), msg.get('fournisseur', ''), msg.get('modele', ''), msg.get('couleur', '#333'), msg.get('execution_time', '')))
|
messages_ui.append(render_user_msg(msg['content']))
|
||||||
|
else:
|
||||||
|
messages_ui.append(render_ai_msg(
|
||||||
|
msg['content'],
|
||||||
|
msg.get('intention', ''),
|
||||||
|
msg.get('fournisseur', ''),
|
||||||
|
msg.get('modele', ''),
|
||||||
|
msg.get('couleur', '#333'),
|
||||||
|
msg.get('execution_time', ''),
|
||||||
|
msg.get('trace', []) # <--- C'est ici qu'on transmet la trace aux anciens messages !
|
||||||
|
))
|
||||||
if messages_ui: messages_ui.append(Script("setTimeout(renderMarkdown, 150);"))
|
if messages_ui: messages_ui.append(Script("setTimeout(renderMarkdown, 150);"))
|
||||||
|
|
||||||
auto_refresh_script = Script('''
|
auto_refresh_script = Script('''
|
||||||
|
|||||||
Reference in new issue
Block a user