J'ai finalement retravailler la feuille de style des log.
This commit is contained in:
@@ -7,17 +7,17 @@ import (
|
||||
|
||||
func (d *DIDLLite) ToMarkdown() string {
|
||||
var buf strings.Builder
|
||||
buf.WriteString("# DIDL-Lite Document\n\n")
|
||||
buf.WriteString("### DIDL-Lite Document\n\n")
|
||||
|
||||
if len(d.Containers) > 0 {
|
||||
buf.WriteString("## Containers\n\n")
|
||||
buf.WriteString("#### Containers\n\n")
|
||||
for _, c := range d.Containers {
|
||||
c.markdown(&buf, 0)
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.Items) > 0 {
|
||||
buf.WriteString("## Items\n\n")
|
||||
buf.WriteString("#### Items\n\n")
|
||||
for _, i := range d.Items {
|
||||
i.markdown(&buf, 0)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
|
||||
<style>
|
||||
:root {
|
||||
/* 🎨 Dark theme (default) */
|
||||
--bg-primary: #0d1117;
|
||||
--bg-secondary: #161b22;
|
||||
--border: #30363d;
|
||||
@@ -17,12 +18,26 @@
|
||||
--debug: #8957e5;
|
||||
--success: #3fb950;
|
||||
}
|
||||
body.light {
|
||||
/* 🎨 Light theme */
|
||||
--bg-primary: #f6f8fa;
|
||||
--bg-secondary: #ffffff;
|
||||
--border: #d0d7de;
|
||||
--text-primary: #24292f;
|
||||
--text-secondary: #57606a;
|
||||
--error: #cf222e;
|
||||
--warning: #9a6700;
|
||||
--info: #0969da;
|
||||
--debug: #8250df;
|
||||
--success: #1a7f37;
|
||||
}
|
||||
body {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
transition: background 0.3s, color 0.3s;
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
@@ -63,6 +78,7 @@
|
||||
border-left: 4px solid;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
transition: background 0.3s;
|
||||
}
|
||||
.log.error { border-color: var(--error); }
|
||||
.log.warning { border-color: var(--warning); }
|
||||
@@ -86,7 +102,6 @@
|
||||
.level-debug { background: var(--debug); color: white; }
|
||||
.log-content {
|
||||
margin-top: 6px;
|
||||
white-space: pre-wrap;
|
||||
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono, Menlo, monospace;
|
||||
}
|
||||
.log-fields {
|
||||
@@ -144,6 +159,97 @@
|
||||
padding: 0 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 🔥 Markdown styling */
|
||||
.log-content a {
|
||||
color: var(--info);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.log-content details {
|
||||
margin: 6px 0;
|
||||
padding: 6px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
}
|
||||
.log-content pre code {
|
||||
display: block;
|
||||
padding: 8px;
|
||||
background: var(--bg-primary);
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.log-content h1,
|
||||
.log-content h2,
|
||||
.log-content h3 {
|
||||
color: var(--info);
|
||||
margin-top: 10px;
|
||||
}
|
||||
.log-content ul {
|
||||
margin-left: 20px;
|
||||
list-style: disc;
|
||||
margin-block-start: 0em;
|
||||
margin-block-end: 0em;
|
||||
}
|
||||
|
||||
.log-content p {
|
||||
margin-block-start: 0em;
|
||||
margin-block-end: 0em;
|
||||
}
|
||||
|
||||
/* 🌗 Toggle theme button */
|
||||
#theme-toggle {
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
#theme-toggle:hover {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
/* Style global des <details> */
|
||||
.log details {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
padding-left: 60px; /* Indentation à gauche */
|
||||
border-left: 2px solid var(--border); /* Ligne pour visualiser l'indent */
|
||||
}
|
||||
|
||||
/* Style du <summary> */
|
||||
.log summary {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
list-style: none;
|
||||
color: var(--info);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* Éviter que le triangle de <summary> déforme le texte */
|
||||
.log summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Optionnel : triangle personnalisé */
|
||||
.log summary::before {
|
||||
content: "▶️";
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
/* Triangle pivotant quand ouvert */
|
||||
.log details[open] summary::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Contenu du <details> */
|
||||
.log details > *:not(summary) {
|
||||
margin-left: 12px; /* Décalage supplémentaire pour le contenu interne */
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
@@ -163,6 +269,7 @@
|
||||
<label><input type="checkbox" id="autoscroll" checked> Auto-scroll</label>
|
||||
<button id="clear">Clear</button>
|
||||
<button id="export">Export</button>
|
||||
<button id="theme-toggle">🌙 Dark</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="status">
|
||||
@@ -173,11 +280,25 @@
|
||||
</header>
|
||||
<div id="logs"></div>
|
||||
<script>
|
||||
// 🎨 Theme toggle
|
||||
const themeToggle = document.getElementById("theme-toggle");
|
||||
const logsContainer = document.getElementById('logs');
|
||||
const statusIndicator = document.getElementById('status-indicator');
|
||||
const statusText = document.getElementById('status-text');
|
||||
const logCountElement = document.getElementById('log-count');
|
||||
const maxLogs = 1000;
|
||||
function updateTheme() {
|
||||
if (document.body.classList.contains("light")) {
|
||||
themeToggle.textContent = "🌙 Dark";
|
||||
} else {
|
||||
themeToggle.textContent = "☀️ Light";
|
||||
}
|
||||
}
|
||||
themeToggle.addEventListener("click", () => {
|
||||
document.body.classList.toggle("light");
|
||||
updateTheme();
|
||||
});
|
||||
updateTheme();
|
||||
let eventSource = null;
|
||||
let filters = {
|
||||
error: true,
|
||||
|
||||
69
soap/markdown.go
Normal file
69
soap/markdown.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package soap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Markdownable représente un objet pouvant se transformer en Markdown
|
||||
type Markdownable interface {
|
||||
ToMarkdown() string
|
||||
}
|
||||
|
||||
// ToMarkdown convertit l'action et ses arguments en Markdown lisible
|
||||
func (ar *ActionRequest) ToMarkdown() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Titre de l'action
|
||||
buf.WriteString(fmt.Sprintf("➡️ SOAP Action: %s\n\n", ar.Name))
|
||||
|
||||
for key, val := range ar.Args {
|
||||
buf.WriteString(fmt.Sprintf("- **%s**: ", key))
|
||||
|
||||
var content string
|
||||
if md, ok := val.(Markdownable); ok {
|
||||
content = md.ToMarkdown()
|
||||
} else {
|
||||
content = fmt.Sprintf("%v", val)
|
||||
}
|
||||
|
||||
// Si contenu long ou multi-lignes
|
||||
if strings.Contains(content, "\n") || len(content) > 60 {
|
||||
// Résumé : première ligne ou URL tronquée
|
||||
summary := firstLineOrTruncate(content, 60)
|
||||
buf.WriteString(fmt.Sprintf("`%s`\n", summary))
|
||||
buf.WriteString("<details>\n\n")
|
||||
|
||||
// Conserver Markdown complet, sans indentation
|
||||
buf.WriteString(content)
|
||||
if !strings.HasSuffix(content, "\n") {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
buf.WriteString("</details>\n\n")
|
||||
} else if isURL(content) {
|
||||
buf.WriteString(fmt.Sprintf("[%s](%s)\n", content, content))
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf("`%s`\n", content))
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Tronque la première ligne ou l'URL si trop longue
|
||||
func firstLineOrTruncate(s string, max int) string {
|
||||
lines := strings.SplitN(s, "\n", 2)
|
||||
first := lines[0]
|
||||
if len(first) > max {
|
||||
return first[:max] + "…"
|
||||
}
|
||||
|
||||
first = strings.TrimLeft(first, "# ")
|
||||
return first
|
||||
}
|
||||
|
||||
// Vérifie si une string ressemble à une URL
|
||||
func isURL(s string) bool {
|
||||
return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://")
|
||||
}
|
||||
@@ -223,10 +223,7 @@ USN: uuid:%s::%s
|
||||
if _, err := s.conn.WriteToUDP([]byte(resp), src); err != nil {
|
||||
log.Warnf("❌ Failed to send M-SEARCH response to %v: %v", src, err)
|
||||
} else {
|
||||
// log.Warnf("✅ M-Search response sent : %x", resp)
|
||||
// log.Warnf("✅ M-Search response sent : %s", resp)
|
||||
|
||||
log.Infof("📡 Responded to M-SEARCH from %v with ST=%s", src, st)
|
||||
log.Infof("📡 Responded to M-SEARCH from %v with ST=%s\n<details>\n\n```\n%s\n```\n</details>\n\n", src, st, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func _AVTransportURIMetaDataParser(value string) (interface{}, error) {
|
||||
log.Warnf("[avtransport] Parsing AVTransport)")
|
||||
log.Debug("[avtransport] Parsing AVTransport)")
|
||||
didl, err := didl.Parse(value)
|
||||
if err != nil {
|
||||
return value, err
|
||||
|
||||
@@ -160,7 +160,7 @@ func (svc *ServiceInstance) ControlHandler() func(w http.ResponseWriter, r *http
|
||||
// 1️⃣ Chercher si le param correspond à une StateVarInstance
|
||||
|
||||
sv, ok := svc.statevariables[param]
|
||||
log.Warnf("Look for a variable named : %s -> %v", param, ok)
|
||||
log.Debugf("Look for a variable named : %s -> %v", param, ok)
|
||||
|
||||
if ok {
|
||||
if sv.HasParser() {
|
||||
@@ -221,13 +221,7 @@ func (svc *ServiceInstance) ControlHandler() func(w http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("➡️ SOAP Action: %s", req.Name)
|
||||
for k, v := range req.Args {
|
||||
if mi, ok := v.(Markdownable); ok {
|
||||
v = mi.ToMarkdown()
|
||||
}
|
||||
log.Infof(" %s:%s = %v", req.Name, k, v)
|
||||
}
|
||||
log.Info(req.ToMarkdown())
|
||||
|
||||
// Ici tu peux appeler l'action correspondante sur svc.actions[req.Name] et récupérer le résultat
|
||||
// Exemple de réponse minimale :
|
||||
|
||||
Reference in New Issue
Block a user