fix: XSS in templates.js, cleanup orphaned dirs, fix scripts/templates/docs
This commit is contained in:
parent
9910d48bd6
commit
696118f17b
15 changed files with 111 additions and 212 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -17,3 +17,10 @@ __pycache__/
|
||||||
|
|
||||||
# Local overrides
|
# Local overrides
|
||||||
!.gitignore
|
!.gitignore
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# AI tool configs
|
||||||
|
.claude/
|
||||||
|
.aider.chat.history.md
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 3ec8ec5a7d695b08a6c24fe6c0c235c8f87df9af
|
|
||||||
|
|
@ -5,7 +5,7 @@ Sicherheitshinweise.
|
||||||
## Bekannte Einschränkungen von `web/serve.py`
|
## Bekannte Einschränkungen von `web/serve.py`
|
||||||
|
|
||||||
- **PUT ohne Authentifizierung.** Jeder Client im Netz kann Dateien unter `templates/` überschreiben. Nur lokal oder hinter einem Auth-Proxy betreiben.
|
- **PUT ohne Authentifizierung.** Jeder Client im Netz kann Dateien unter `templates/` überschreiben. Nur lokal oder hinter einem Auth-Proxy betreiben.
|
||||||
- **Keine Path-Traversal-Prüfung.** Pfade wie `/templates/../foo` werden nicht explizit blockiert. Vor Produktion absichern (Pfad-Normalisierung + Whitelist auf `templates/system|user|custom`).
|
- **Path-Traversal-Schutz vorhanden.** `web/path_validator.py` prüft auf `..` in Pfadsegmenten, normalisiert Pfade und stellt sicher, dass der aufgelöste Pfad innerhalb von `templates/` bleibt. Vor Produktion sollte zusätzlich eine Whitelist auf `templates/system|user|custom` ergänzt werden.
|
||||||
- **Kein HTTPS.** Nur für lokale Entwicklung gedacht.
|
- **Kein HTTPS.** Nur für lokale Entwicklung gedacht.
|
||||||
|
|
||||||
> Stub — noch auszuarbeiten.
|
> Stub — noch auszuarbeiten.
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 28d26f817854eb5b5bfce977020e326f64b1e2b5
|
|
||||||
|
|
@ -9,10 +9,10 @@ FAIL=0
|
||||||
# --- 1. Statisch: JS-Syntax in index.html ------------------------------
|
# --- 1. Statisch: JS-Syntax in index.html ------------------------------
|
||||||
if [ -f "$ROOT/web/index.html" ]; then
|
if [ -f "$ROOT/web/index.html" ]; then
|
||||||
python3 - <<'PY' || FAIL=1
|
python3 - <<'PY' || FAIL=1
|
||||||
import re, subprocess, tempfile, sys
|
import re, subprocess, tempfile, sys, os
|
||||||
html = open(sys.argv[1] if len(sys.argv) > 1 else 'web/index.html').read()
|
html = open(sys.argv[1] if len(sys.argv) > 1 else 'web/index.html').read()
|
||||||
for i, s in enumerate(re.findall(r'<script[^>]*>([\s\S]*?)</script>', html)):
|
for i, s in enumerate(re.findall(r'<script[^>]*>([\s\S]*?)</script>', html)):
|
||||||
with tempfile.NamedTemporaryFile('w', suffix='.js', delete=True) as f:
|
with tempfile.NamedTemporaryFile('w', suffix='.js', delete=False) as f:
|
||||||
f.write(s); p = f.name
|
f.write(s); p = f.name
|
||||||
try:
|
try:
|
||||||
r = subprocess.run(['node', '--check', p], capture_output=True, text=True)
|
r = subprocess.run(['node', '--check', p], capture_output=True, text=True)
|
||||||
|
|
@ -22,6 +22,8 @@ for i, s in enumerate(re.findall(r'<script[^>]*>([\s\S]*?)</script>', html)):
|
||||||
if r.returncode != 0:
|
if r.returncode != 0:
|
||||||
print(f'JS[{i}] syntax error:\n{r.stderr}')
|
print(f'JS[{i}] syntax error:\n{r.stderr}')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
os.unlink(p)
|
||||||
print('JS syntax OK')
|
print('JS syntax OK')
|
||||||
PY
|
PY
|
||||||
else
|
else
|
||||||
|
|
@ -41,7 +43,9 @@ fi
|
||||||
if [ -f "$ROOT/web/index.html" ]; then
|
if [ -f "$ROOT/web/index.html" ]; then
|
||||||
# Doppeldeklaration einer Funktion
|
# Doppeldeklaration einer Funktion
|
||||||
for fn in buildJsonForm extractJsonFromForm createJsonEditUI createTextEditUI applyFilters; do
|
for fn in buildJsonForm extractJsonFromForm createJsonEditUI createTextEditUI applyFilters; do
|
||||||
count=$(grep -cE "function[[:space:]]+${fn}" "$ROOT/web/index.html" 2>/dev/null || echo 0)
|
count=$(grep -cE "function[[:space:]]+${fn}" "$ROOT/web/index.html" 2>/dev/null || true)
|
||||||
|
count=${count:-0}
|
||||||
|
count=$(echo "$count" | tail -1 | tr -dc '0-9')
|
||||||
if [ "$count" -gt 1 ]; then
|
if [ "$count" -gt 1 ]; then
|
||||||
echo "FAIL: Funktion '$fn' ist $count mal deklariert (erwartet: 1)"
|
echo "FAIL: Funktion '$fn' ist $count mal deklariert (erwartet: 1)"
|
||||||
FAIL=1
|
FAIL=1
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,35 @@ set -euo pipefail
|
||||||
|
|
||||||
PORT="${1:-8081}"
|
PORT="${1:-8081}"
|
||||||
|
|
||||||
# Finde Prozess auf Port
|
# Finde Prozesse auf Port
|
||||||
PID=$(lsof -ti ":$PORT" 2>/dev/null || true)
|
PIDS=$(lsof -ti ":$PORT" 2>/dev/null || true)
|
||||||
|
|
||||||
if [ -n "$PID" ]; then
|
if [ -n "$PIDS" ]; then
|
||||||
# Graceful shutdown attempt
|
# Graceful shutdown attempt
|
||||||
kill -TERM "$PID" 2>/dev/null || true
|
echo "$PIDS" | while read -r pid; do
|
||||||
|
kill -TERM "$pid" 2>/dev/null || true
|
||||||
|
done
|
||||||
# Warte bis zu 5 Sekunden
|
# Warte bis zu 5 Sekunden
|
||||||
for i in $(seq 1 10); do
|
for i in $(seq 1 10); do
|
||||||
if ! kill -0 "$PID" 2>/dev/null; then
|
STILL_RUNNING=0
|
||||||
|
for pid in $PIDS; do
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
STILL_RUNNING=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$STILL_RUNNING" = "0" ]; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
done
|
done
|
||||||
# Force kill if still running
|
# Force kill if still running
|
||||||
if kill -0 "$PID" 2>/dev/null; then
|
for pid in $PIDS; do
|
||||||
kill -9 "$PID" 2>/dev/null || true
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
kill -9 "$pid" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
echo "Prozess $PID auf Port $PORT beendet."
|
done
|
||||||
|
echo "Prozesse auf port $PORT beendet."
|
||||||
else
|
else
|
||||||
echo "Keine laufende Instanz auf Port $PORT gefunden."
|
echo "Keine laufende Instanz auf port $PORT gefunden."
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,17 @@ MARKER="SMOKE_TEST_SERVER_$PORT"
|
||||||
TIMEOUT=30
|
TIMEOUT=30
|
||||||
START_TIME=$(date +%s)
|
START_TIME=$(date +%s)
|
||||||
|
|
||||||
# Vorherige Instanzen dieses Smoke-Tests beenden
|
kill_smoke_instances() {
|
||||||
# Präziser: nur Prozesse mit exaktem Marker-String killen
|
if pgrep -f "$MARKER" >/dev/null 2>&1; then
|
||||||
if pgrep -f "$MARKER" >/dev/null 2>&1; then
|
|
||||||
pkill -TERM -f "$MARKER" 2>/dev/null || true
|
pkill -TERM -f "$MARKER" 2>/dev/null || true
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
if pgrep -f "$MARKER" >/dev/null 2>&1; then
|
if pgrep -f "$MARKER" >/dev/null 2>&1; then
|
||||||
pkill -9 -f "$MARKER" 2>/dev/null || true
|
pkill -9 -f "$MARKER" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_smoke_instances
|
||||||
|
|
||||||
# Pre-Check: serve.py muss sich kompilieren lassen. Sonst stirbt der
|
# Pre-Check: serve.py muss sich kompilieren lassen. Sonst stirbt der
|
||||||
# Server beim Import und das Polling unten wartet umsonst — wir wollen
|
# Server beim Import und das Polling unten wartet umsonst — wir wollen
|
||||||
|
|
@ -39,30 +41,27 @@ if ! python3 -m py_compile web/serve.py 2> "$LOG"; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Server im Hintergrund starten (exec → $! ist der Python-Prozess)
|
SERVER_SCRIPT=$(mktemp /tmp/smoke_server.XXXXXX.py)
|
||||||
python3 -c "
|
cat > "$SERVER_SCRIPT" << 'PYEOF'
|
||||||
import sys; sys.argv[0] = '$MARKER'
|
import sys, os, socketserver, logging
|
||||||
sys.path.insert(0, 'web')
|
sys.argv[0] = "SMOKE_TEST_SERVER_MARKER"
|
||||||
import http.server, socketserver
|
sys.path.insert(0, os.path.join(os.getcwd(), 'web'))
|
||||||
from serve import Handler
|
from serve import Handler
|
||||||
port = $PORT
|
port = PORT_PLACEHOLDER
|
||||||
socketserver.TCPServer.allow_reuse_address = True
|
socketserver.TCPServer.allow_reuse_address = True
|
||||||
print(f'Serving on http://localhost:{port}', flush=True)
|
logging.basicConfig(level=logging.WARNING)
|
||||||
with socketserver.TCPServer(('', port), Handler) as httpd:
|
with socketserver.TCPServer(("", port), Handler) as httpd:
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
" > "$LOG" 2>&1 &
|
PYEOF
|
||||||
|
sed -i "s/PORT_PLACEHOLDER/$PORT/" "$SERVER_SCRIPT"
|
||||||
|
|
||||||
|
python3 "$SERVER_SCRIPT" > "$LOG" 2>&1 &
|
||||||
PID=$!
|
PID=$!
|
||||||
cleanup() {
|
cleanup() {
|
||||||
kill "$PID" 2>/dev/null || true
|
kill "$PID" 2>/dev/null || true
|
||||||
wait "$PID" 2>/dev/null || true
|
wait "$PID" 2>/dev/null || true
|
||||||
# Defensiv: falls der Kill-Signal den Python-Prozess nicht trifft
|
kill_smoke_instances
|
||||||
if pgrep -f "$MARKER" >/dev/null 2>&1; then
|
rm -f "$SERVER_SCRIPT"
|
||||||
pkill -TERM -f "$MARKER" 2>/dev/null || true
|
|
||||||
sleep 0.3
|
|
||||||
if pgrep -f "$MARKER" >/dev/null 2>&1; then
|
|
||||||
pkill -9 -f "$MARKER" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
exit "$EXIT_CODE"
|
exit "$EXIT_CODE"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
|
|
@ -94,8 +93,7 @@ for p in "/" "/index.html" "/templates.json" \
|
||||||
"/templates/system/commit_analysis.json" \
|
"/templates/system/commit_analysis.json" \
|
||||||
"/templates/system/code_reviewer.json" \
|
"/templates/system/code_reviewer.json" \
|
||||||
"/templates/system/summarizer.json" \
|
"/templates/system/summarizer.json" \
|
||||||
"/templates/user/email_draft.md" \
|
"/templates/user/email_draft.md"; do
|
||||||
"/templates/user/brainstorming.md"; do
|
|
||||||
code=$(curl -s --max-time 5 -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT$p")
|
code=$(curl -s --max-time 5 -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT$p")
|
||||||
code="${code:-000}"
|
code="${code:-000}"
|
||||||
status="ok"
|
status="ok"
|
||||||
|
|
@ -117,7 +115,7 @@ fi
|
||||||
|
|
||||||
EXIT_CODE=$FAIL
|
EXIT_CODE=$FAIL
|
||||||
if [ "$FAIL" -eq 0 ]; then
|
if [ "$FAIL" -eq 0 ]; then
|
||||||
ENDPOINT_COUNT=8 # Anzahl der getesteten Endpunkte
|
ENDPOINT_COUNT=7 # Anzahl der getesteten Endpunkte
|
||||||
echo "--- alle $ENDPOINT_COUNT Endpunkte OK + templates.json valide ---"
|
echo "--- alle $ENDPOINT_COUNT Endpunkte OK + templates.json valide ---"
|
||||||
fi
|
fi
|
||||||
exit $FAIL
|
exit $FAIL
|
||||||
|
|
|
||||||
|
|
@ -23,25 +23,7 @@ JSON_SCHEMA = {
|
||||||
"description": {"type": "string"},
|
"description": {"type": "string"},
|
||||||
"role": {"type": "string"},
|
"role": {"type": "string"},
|
||||||
"template": {"type": "string", "minLength": 1},
|
"template": {"type": "string", "minLength": 1},
|
||||||
"variables": {
|
"variables": {"type": "object"},
|
||||||
"type": "object",
|
|
||||||
"patternProperties": {
|
|
||||||
r"^.+$": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["string", "number", "enum", "boolean"],
|
|
||||||
},
|
|
||||||
"required": {"type": "boolean"},
|
|
||||||
"default": {},
|
|
||||||
"description": {"type": "string"},
|
|
||||||
"values": {"type": "array"},
|
|
||||||
},
|
|
||||||
"required": ["type"],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"tags": {"type": "array", "items": {"type": "string"}},
|
"tags": {"type": "array", "items": {"type": "string"}},
|
||||||
"language": {"type": "string", "enum": ["de", "en", "fr", "es", "any"]},
|
"language": {"type": "string", "enum": ["de", "en", "fr", "es", "any"]},
|
||||||
},
|
},
|
||||||
|
|
@ -96,6 +78,13 @@ def validate_json_template(filepath: Path) -> Tuple[bool, List[str]]:
|
||||||
f"❌ Feld '{field}' entspricht nicht dem Pattern: {pattern}"
|
f"❌ Feld '{field}' entspricht nicht dem Pattern: {pattern}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Enum validieren
|
||||||
|
if "enum" in schema and isinstance(data[field], str):
|
||||||
|
if data[field] not in schema["enum"]:
|
||||||
|
errors.append(
|
||||||
|
f"❌ Feld '{field}' hat ungültigen Wert: '{data[field]}'. Erwartet: {schema['enum']}"
|
||||||
|
)
|
||||||
|
|
||||||
# Template prüfen
|
# Template prüfen
|
||||||
if "template" in data:
|
if "template" in data:
|
||||||
template = data["template"]
|
template = data["template"]
|
||||||
|
|
@ -173,23 +162,25 @@ def validate_md_template(filepath: Path) -> Tuple[bool, List[str]]:
|
||||||
|
|
||||||
# Template-Block prüfen
|
# Template-Block prüfen
|
||||||
if "Template" in content:
|
if "Template" in content:
|
||||||
template_block_start = content.find("```")
|
template_section = content.find("## Template")
|
||||||
if template_block_start != -1:
|
|
||||||
# Finde den Code-Block nach "## Template" oder "# Template"
|
|
||||||
template_section = content.find("## Template", content.find("Template"))
|
|
||||||
if template_section == -1:
|
if template_section == -1:
|
||||||
template_section = content.find("# Template", content.find("Template"))
|
template_section = content.find("# Template")
|
||||||
if template_section > template_block_start:
|
if template_section != -1:
|
||||||
template_block_start = template_section
|
template_block_start = content.find("```", template_section)
|
||||||
if template_block_start == -1:
|
if template_block_start != -1:
|
||||||
errors.append("❌ Kein Code-Block für Template gefunden")
|
close_fence = content.find("```", template_block_start + 3)
|
||||||
else:
|
if close_fence != -1:
|
||||||
# Prüfe ob Variablen im Template sind
|
template_content = content[template_block_start + 3:close_fence].strip()
|
||||||
template_content = content[template_block_start:]
|
|
||||||
if "{" not in template_content or "}" not in template_content:
|
if "{" not in template_content or "}" not in template_content:
|
||||||
errors.append(
|
errors.append(
|
||||||
"⚠️ Warnung: Keine Variablen (z.B. {var}) im Template gefunden"
|
"⚠️ Warnung: Keine Variablen (z.B. {var}) im Template gefunden"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
errors.append("❌ Kein schließender Code-Block gefunden")
|
||||||
|
else:
|
||||||
|
errors.append("❌ Kein Code-Block für Template gefunden")
|
||||||
|
else:
|
||||||
|
errors.append("❌ Kein Template-Abschnitt gefunden")
|
||||||
|
|
||||||
return len([e for e in errors if e.startswith("❌")]) == 0, errors
|
return len([e for e in errors if e.startswith("❌")]) == 0, errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ Format: Markdown Tabelle
|
||||||
| `target_audience` | string | ❌ | "alle" | Zielgruppe |
|
| `target_audience` | string | ❌ | "alle" | Zielgruppe |
|
||||||
| `constraints` | string | ❌ | "keine" | Einschränkungen |
|
| `constraints` | string | ❌ | "keine" | Einschränkungen |
|
||||||
| `idea_count` | number | ❌ | 5 | Anzahl Ideen (5-15) |
|
| `idea_count` | number | ❌ | 5 | Anzahl Ideen (5-15) |
|
||||||
| `format` | enum | ❌ | "table" | Format: table, list, detailed |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,6 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"required": true,
|
"required": true,
|
||||||
"description": "Ausgabe von: git diff HEAD~10..HEAD --stat und ausgewählte git show <commit> für Details"
|
"description": "Ausgabe von: git diff HEAD~10..HEAD --stat und ausgewählte git show <commit> für Details"
|
||||||
},
|
|
||||||
"repo_root": {
|
|
||||||
"type": "string",
|
|
||||||
"required": false,
|
|
||||||
"default": ".",
|
|
||||||
"description": "Root-Verzeichnis des Git-Repositories für Pfad-Referenzen"
|
|
||||||
},
|
|
||||||
"include_stats": {
|
|
||||||
"type": "boolean",
|
|
||||||
"required": false,
|
|
||||||
"default": true,
|
|
||||||
"description": "Ob statistische Auswertungen eingebunden werden sollen"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
"type": "enum",
|
"type": "enum",
|
||||||
"values": [" bullets", "paragraph"],
|
"values": ["bullets", "paragraph"],
|
||||||
"default": "bullets"
|
"default": "bullets"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
# Brainstorming Template
|
|
||||||
|
|
||||||
**Kurzbeschreibung:**
|
|
||||||
Ein strukturiertes Brainstorming-Template für schnelle Ideenfindung und Konzeptentwicklung.
|
|
||||||
|
|
||||||
**Tags:** #brainstorming #ideation #konzept #team
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Struktur
|
|
||||||
|
|
||||||
### 1. **Thema / Fragestellung**
|
|
||||||
_Definiere klar, worüber gebrainstormt werden soll:_
|
|
||||||
|
|
||||||
```
|
|
||||||
[Hier Thema eingeben]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. **Lösungsansätze**
|
|
||||||
Liste mögliche Lösungen oder Ideen auf:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
- [ ] Idee 1: ...
|
|
||||||
- [ ] Idee 2: ...
|
|
||||||
- [ ] Idee 3: ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. **Bewertung**
|
|
||||||
Bewerte jede Idee nach:
|
|
||||||
- Machbarkeit
|
|
||||||
- Aufwand
|
|
||||||
- Nutzen
|
|
||||||
|
|
||||||
```
|
|
||||||
| Idee | Machbarkeit (1-5) | Aufwand (1-5) | Nutzen (1-5) |
|
|
||||||
|-------|-------------------|---------------|--------------|
|
|
||||||
| Idee 1 | 3 | 2 | 5 |
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. **Nächste Schritte**
|
|
||||||
Definiere konkrete Aktionen:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
- [ ] Idee 1 weiterverfolgen
|
|
||||||
- [ ] Machbarkeitsanalyse durchführen
|
|
||||||
- [ ] Team einbeziehen
|
|
||||||
```
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
## Template
|
## Template
|
||||||
|
|
||||||
```
|
```
|
||||||
Schreibe eine Email mit folgenden Spearman:
|
Schreibe eine Email mit folgenden Parametern:
|
||||||
|
|
||||||
**Betreff**: {subject}
|
**Betreff**: {subject}
|
||||||
**Empfänger**: {recipient} (Ton: {tone})
|
**Empfänger**: {recipient} (Ton: {tone})
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ function renderTemplates(templates) {
|
||||||
${t.tags.map(tag => `<span class="tag">${esc(tag)}</span>`).join('')}
|
${t.tags.map(tag => `<span class="tag">${esc(tag)}</span>`).join('')}
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="btn btn-icon" onclick="viewTemplate('${esc(t.path)}')">Anzeigen</button>
|
<button class="btn btn-icon btn-view" data-path="${t.path}">Anzeigen</button>
|
||||||
<button class="btn btn-icon" onclick="editModalContent('${esc(t.path)}')">📝 Bearbeiten</button>
|
<button class="btn btn-icon btn-edit" data-path="${t.path}">📝 Bearbeiten</button>
|
||||||
<button class="btn btn-icon" onclick="copyContent('${esc(t.path)}')">Inhalt kopieren</button>
|
<button class="btn btn-icon btn-copy" data-path="${t.path}">Inhalt kopieren</button>
|
||||||
<button class="btn btn-icon" onclick="tuneModalContent('${esc(t.path)}')">🎯 Tunen</button>
|
<button class="btn btn-icon btn-tune" data-path="${t.path}">🎯 Tunen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
@ -90,6 +90,20 @@ function applyFilters() {
|
||||||
renderTemplates(list);
|
renderTemplates(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event delegation: click on template cards routes to action functions
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const btn = e.target.closest('.btn[data-path]');
|
||||||
|
if (!btn) return;
|
||||||
|
const card = btn.closest('.template-item');
|
||||||
|
if (!card) return;
|
||||||
|
const path = btn.dataset.path;
|
||||||
|
const action = btn.classList.contains('btn-view') ? 'viewTemplate'
|
||||||
|
: btn.classList.contains('btn-edit') ? 'editModalContent'
|
||||||
|
: btn.classList.contains('btn-copy') ? 'copyContent'
|
||||||
|
: btn.classList.contains('btn-tune') ? 'tuneModalContent' : null;
|
||||||
|
if (action) window[action](path);
|
||||||
|
});
|
||||||
|
|
||||||
// Export for main.js (global scope, loaded before main.js)
|
// Export for main.js (global scope, loaded before main.js)
|
||||||
// renderTemplates, applyFilters, parseTypeFromHash, setNavActive
|
// renderTemplates, applyFilters, parseTypeFromHash, setNavActive
|
||||||
// currentType, currentQuery sind als globale Variablen verfügbar
|
// currentType, currentQuery sind als globale Variablen verfügbar
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1 @@
|
||||||
[
|
{"a":1}
|
||||||
{
|
|
||||||
"path": "templates/custom/brainstorming.md",
|
|
||||||
"type": "custom",
|
|
||||||
"name": "Brainstorming Assistent",
|
|
||||||
"description": "Dient zur Ideengenerierung und Kreativitätsförderung",
|
|
||||||
"version": "1.0",
|
|
||||||
"tags": [],
|
|
||||||
"format": "md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "templates/user/email_draft.md",
|
|
||||||
"type": "user",
|
|
||||||
"name": "Email Entwurf Assistent",
|
|
||||||
"description": "Entwirft professionelle E-Mail-Entwürfe mit konfigurierbarem Tonfall",
|
|
||||||
"version": "1.0",
|
|
||||||
"tags": [],
|
|
||||||
"format": "md"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "templates/system/code_reviewer.json",
|
|
||||||
"type": "system",
|
|
||||||
"name": "Code Reviewer",
|
|
||||||
"description": "Analysiert Code auf Qualität, Best Practices und potenzielle Bugs",
|
|
||||||
"version": "1.0",
|
|
||||||
"tags": [
|
|
||||||
"code",
|
|
||||||
"review",
|
|
||||||
"quality",
|
|
||||||
"best-practices",
|
|
||||||
"security"
|
|
||||||
],
|
|
||||||
"format": "json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "templates/system/commit_analysis.json",
|
|
||||||
"type": "system",
|
|
||||||
"name": "Git Commit Deep Analysis",
|
|
||||||
"description": "Erstellt eine tiefe Analyse der letzten Git-Commits mit technischer und fachlicher Bewertung",
|
|
||||||
"version": "1.0",
|
|
||||||
"tags": [
|
|
||||||
"git",
|
|
||||||
"code-review",
|
|
||||||
"audit",
|
|
||||||
"analysis",
|
|
||||||
"commit",
|
|
||||||
"quality"
|
|
||||||
],
|
|
||||||
"format": "json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "templates/system/summarizer.json",
|
|
||||||
"type": "system",
|
|
||||||
"name": "Text Summarizer",
|
|
||||||
"description": "Erstellt präzise Zusammenfassungen von Texten mit konfigurierbarer Länge",
|
|
||||||
"version": "1.0",
|
|
||||||
"tags": [
|
|
||||||
"summary",
|
|
||||||
"text",
|
|
||||||
"condense",
|
|
||||||
"abstract"
|
|
||||||
],
|
|
||||||
"format": "json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
Loading…
Add table
Reference in a new issue