forensic-pathways/dfir_yaml_editor.html
2025-07-26 13:42:48 +02:00

1610 lines
68 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DFIR Tools YAML Editor</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #3498db;
--secondary: #95a5a6;
--success: #27ae60;
--warning: #f39c12;
--danger: #e74c3c;
--dark: #2c3e50;
--light: #ecf0f1;
--border: #dee2e6;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, var(--dark) 0%, var(--primary) 100%);
color: white;
padding: 25px;
text-align: center;
}
.tabs {
background: var(--light);
display: flex;
border-bottom: 2px solid var(--border);
}
.tab {
padding: 15px 25px;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
font-weight: 500;
}
.tab:hover {
background: var(--border);
}
.tab.active {
background: white;
border-bottom-color: var(--primary);
color: var(--primary);
}
.tab-content {
display: none;
padding: 30px;
min-height: 600px;
}
.tab-content.active {
display: block;
}
.btn {
background: var(--primary);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
margin: 5px;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
}
.btn-secondary { background: var(--secondary); }
.btn-danger { background: var(--danger); }
.btn-success { background: var(--success); }
.btn-warning { background: var(--warning); }
.search-container {
background: var(--light);
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.search-input {
width: 100%;
padding: 12px;
border: 2px solid var(--border);
border-radius: 8px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: var(--primary);
}
.search-results-info {
margin-top: 10px;
font-size: 14px;
color: var(--secondary);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: var(--primary);
color: white;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.tools-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}
.tool-card {
background: white;
border: 1px solid var(--border);
border-radius: 10px;
padding: 20px;
transition: all 0.3s ease;
}
.tool-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.tool-card.software { border-left: 4px solid var(--primary); }
.tool-card.method { border-left: 4px solid #9b59b6; }
.tool-card.concept { border-left: 4px solid #e67e22; }
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: var(--dark);
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid var(--border);
border-radius: 5px;
font-size: 14px;
}
.checkbox-group {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-top: 10px;
}
.checkbox-item {
display: flex;
align-items: center;
gap: 8px;
}
.tags-input {
display: flex;
flex-wrap: wrap;
gap: 5px;
min-height: 40px;
padding: 5px;
border: 1px solid var(--border);
border-radius: 5px;
background: white;
}
.tag {
background: var(--light);
padding: 4px 8px;
border-radius: 15px;
font-size: 0.85em;
display: flex;
align-items: center;
gap: 5px;
}
.tag-remove {
cursor: pointer;
font-weight: bold;
color: #666;
}
.tag-remove:hover {
color: var(--danger);
}
.bulk-section {
background: var(--light);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.bulk-controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-top: 15px;
}
.message {
padding: 15px;
border-radius: 8px;
margin: 10px 0;
}
.message.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.message.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.hidden { display: none !important; }
.filter-section {
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
margin-top: 15px;
}
@media (max-width: 768px) {
.tabs { flex-direction: column; }
.stats-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.tools-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔧 DFIR Tools YAML Editor</h1>
<p>Enhanced editor for Digital Forensics and Incident Response tools, methods, and concepts</p>
</div>
<div class="tabs">
<div class="tab active" onclick="showTab('overview')">📊 Overview</div>
<div class="tab" onclick="showTab('tools')">🛠️ Tools & Concepts</div>
<div class="tab" onclick="showTab('editor')">✏️ Editor</div>
<div class="tab" onclick="showTab('bulk')">📋 Bulk Edit</div>
<div class="tab" onclick="showTab('knowledge')">📚 Knowledge Generator</div>
<div class="tab" onclick="showTab('export')">💾 Export</div>
</div>
<!-- Overview Tab -->
<div id="overview" class="tab-content active">
<div style="background: var(--light); padding: 20px; border-radius: 10px; margin-bottom: 20px; border: 2px dashed var(--border); text-align: center;">
<h3>📁 Load YAML File</h3>
<input type="file" id="fileInput" accept=".yaml,.yml" onchange="loadFile()">
<p>Select a YAML file to load existing tools data</p>
</div>
<div id="stats" class="stats-grid"></div>
<div id="validationResults" class="hidden">
<h3>🔍 Validation Results</h3>
<div id="validationContent"></div>
</div>
<div style="text-align: center; margin-top: 20px;">
<button class="btn" onclick="validateYAML()">🔍 Validate YAML</button>
<button class="btn btn-secondary" onclick="previewYAML()">👁️ Preview YAML</button>
</div>
</div>
<!-- Tools Tab with Enhanced Search -->
<div id="tools" class="tab-content">
<div class="search-container">
<h3>🔍 Search & Filter Tools</h3>
<input type="text"
id="searchInput"
class="search-input"
placeholder="Search by name, description, tags, or related concepts..."
oninput="applySearch()">
<div class="search-results-info" id="searchResults"></div>
<div class="filter-section">
<label>Type:</label>
<select id="typeFilter" onchange="applyFilters()">
<option value="">All Types</option>
<option value="software">Software</option>
<option value="method">Method</option>
<option value="concept">Concept</option>
</select>
<label>Skill Level:</label>
<select id="skillFilter" onchange="applyFilters()">
<option value="">All Levels</option>
<option value="novice">Novice</option>
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
<option value="advanced">Advanced</option>
<option value="expert">Expert</option>
</select>
<button class="btn btn-secondary" onclick="clearFilters()">Clear All</button>
</div>
</div>
<div id="toolsGrid" class="tools-grid"></div>
</div>
<!-- Editor Tab -->
<div id="editor" class="tab-content">
<h3>✏️ Add/Edit Tool, Method, or Concept</h3>
<form id="toolForm">
<div class="form-group">
<label for="toolType">Type *</label>
<select id="toolType" onchange="toggleConditionalFields()" required>
<option value="software">Software</option>
<option value="method">Method</option>
<option value="concept">Concept</option>
</select>
</div>
<div class="form-group">
<label for="toolName">Name *</label>
<input type="text" id="toolName" required>
</div>
<div class="form-group">
<label for="toolIcon">Icon (Emoji)</label>
<input type="text" id="toolIcon" placeholder="🔧" maxlength="4" style="width: 80px; text-align: center; font-size: 1.5em;">
</div>
<div class="form-group">
<label for="description">Description *</label>
<textarea id="description" required rows="4"></textarea>
</div>
<div class="form-group">
<label for="skillLevel">Skill Level *</label>
<select id="skillLevel" required>
<option value="novice">Novice</option>
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
<option value="advanced">Advanced</option>
<option value="expert">Expert</option>
</select>
</div>
<div class="form-group">
<label for="url">URL *</label>
<input type="url" id="url" required>
</div>
<div class="form-group">
<label>Domains</label>
<div id="domainsCheckbox" class="checkbox-group"></div>
</div>
<div class="form-group">
<label>Phases</label>
<div id="phasesCheckbox" class="checkbox-group"></div>
</div>
<!-- Software/Method specific fields -->
<div id="softwareMethodFields">
<div class="form-group">
<label>Platforms</label>
<div id="platformsCheckbox" class="checkbox-group">
<div class="checkbox-item">
<input type="checkbox" value="Windows" id="platform-windows">
<label for="platform-windows">Windows</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="Linux" id="platform-linux">
<label for="platform-linux">Linux</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="macOS" id="platform-macos">
<label for="platform-macos">macOS</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="Web" id="platform-web">
<label for="platform-web">Web</label>
</div>
<div class="checkbox-item">
<input type="checkbox" value="Cloud" id="platform-cloud">
<label for="platform-cloud">Cloud</label>
</div>
</div>
</div>
<div class="form-group">
<label>Domain-Agnostic Categories</label>
<div id="domainAgnosticCheckbox" class="checkbox-group"></div>
</div>
</div>
<!-- Software specific fields -->
<div id="softwareFields">
<div class="form-group">
<label for="accessType">Access Type</label>
<select id="accessType">
<option value="">Select Access Type</option>
<option value="download">Download</option>
<option value="web">Web Application</option>
<option value="api">API</option>
<option value="cli">Command Line</option>
<option value="service">Service</option>
</select>
</div>
<div class="form-group">
<label for="license">License</label>
<input type="text" id="license" placeholder="e.g., Apache 2.0, MIT, Proprietary">
</div>
<div class="form-group">
<label for="projectUrl">Project URL (CC24 Server)</label>
<input type="url" id="projectUrl">
</div>
<div class="form-group">
<label for="statusUrl">Status URL</label>
<input type="url" id="statusUrl">
</div>
</div>
<div class="form-group">
<div class="checkbox-item">
<input type="checkbox" id="knowledgebase">
<label for="knowledgebase">Has Extended Knowledgebase</label>
</div>
</div>
<div class="form-group">
<label for="tagsInput">Tags</label>
<div class="tags-input" id="tagsInput" onclick="focusTagInput()">
<input type="text" id="tagInputField" placeholder="Add tags..." onkeydown="handleTagInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">
</div>
</div>
<div class="form-group">
<label for="relatedConceptsInput">Related Concepts</label>
<div class="tags-input" id="relatedConceptsInput" onclick="focusRelatedConceptInput()">
<input type="text" id="relatedConceptInputField" placeholder="Add concept names..." onkeydown="handleRelatedConceptInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">
</div>
</div>
<div style="text-align: center; margin-top: 30px;">
<button type="button" class="btn" onclick="saveTool()">💾 Save</button>
<button type="button" class="btn btn-secondary" onclick="clearForm()">🔄 Clear</button>
<button type="button" class="btn btn-warning" onclick="cancelEdit()">❌ Cancel</button>
</div>
</form>
</div>
<!-- Enhanced Bulk Edit Tab -->
<div id="bulk" class="tab-content">
<div class="bulk-section">
<h3>📋 Bulk Operations</h3>
<div id="selectionInfo" style="margin-bottom: 15px; padding: 10px; background: var(--border); border-radius: 5px; font-weight: 500;">
No tools selected
</div>
<div class="bulk-controls">
<button class="btn" onclick="selectAllTools()">Select All</button>
<button class="btn btn-secondary" onclick="clearSelection()">Clear Selection</button>
<button class="btn btn-warning" onclick="bulkSetType()">Set Type</button>
<button class="btn btn-warning" onclick="bulkSetSkillLevel()">Set Skill Level</button>
</div>
<h4 style="margin: 20px 0 10px 0;">Tags Operations</h4>
<div class="bulk-controls">
<button class="btn btn-warning" onclick="bulkAddTags()">Add Tags</button>
<button class="btn btn-danger" onclick="bulkRemoveTags()">Remove Tags</button>
<button class="btn btn-secondary" onclick="bulkReplaceTags()">Replace All Tags</button>
<button class="btn btn-danger" onclick="bulkClearTags()">🗑️ Clear All Tags</button>
</div>
<h4 style="margin: 20px 0 10px 0;">Domain & Phase Operations</h4>
<div class="bulk-controls">
<button class="btn btn-warning" onclick="bulkAddDomains()">Add Domains</button>
<button class="btn btn-danger" onclick="bulkRemoveDomains()">Remove Domains</button>
<button class="btn btn-danger" onclick="bulkClearDomains()">🗑️ Clear All Domains</button>
<button class="btn btn-warning" onclick="bulkAddPhases()">Add Phases</button>
<button class="btn btn-danger" onclick="bulkRemovePhases()">Remove Phases</button>
<button class="btn btn-danger" onclick="bulkClearPhases()">🗑️ Clear All Phases</button>
</div>
<h4 style="margin: 20px 0 10px 0;">Platform Operations</h4>
<div class="bulk-controls">
<button class="btn btn-warning" onclick="bulkAddPlatforms()">Add Platforms</button>
<button class="btn btn-danger" onclick="bulkRemovePlatforms()">Remove Platforms</button>
<button class="btn btn-danger" onclick="bulkClearPlatforms()">🗑️ Clear All Platforms</button>
</div>
<h4 style="margin: 20px 0 10px 0;">Related Concepts Operations</h4>
<div class="bulk-controls">
<button class="btn btn-warning" onclick="bulkAddRelatedConcepts()">Add Related Concepts</button>
<button class="btn btn-danger" onclick="bulkRemoveRelatedConcepts()">Remove Related Concepts</button>
<button class="btn btn-danger" onclick="bulkClearRelatedConcepts()">🗑️ Clear All Related Concepts</button>
</div>
<h4 style="margin: 20px 0 10px 0;">Dangerous Operations</h4>
<div class="bulk-controls">
<button class="btn btn-danger" onclick="bulkDelete()">Delete Selected</button>
</div>
</div>
<div id="bulkToolsGrid" class="tools-grid"></div>
</div>
<!-- Knowledge Generator Tab -->
<div id="knowledge" class="tab-content">
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 20px; margin-top: 20px;">
<h3>📚 Knowledgebase Entry Generator</h3>
<p>Generate properly formatted markdown files for CC24-Hub knowledgebase.</p>
<div class="form-group">
<label for="knowledgeToolSelect">Select Tool/Concept:</label>
<select id="knowledgeToolSelect" onchange="generateKnowledgeTemplate()">
<option value="">Choose a tool or concept...</option>
</select>
</div>
<div id="knowledgePreview" class="hidden">
<h4>Generated Markdown Template:</h4>
<textarea id="markdownContent" readonly style="width: 100%; height: 400px; font-family: monospace; background: var(--light); border: 1px solid var(--border); border-radius: 4px; padding: 15px;"></textarea>
<div style="margin-top: 10px;">
<button class="btn" onclick="downloadMarkdown()">💾 Download Markdown</button>
<button class="btn btn-secondary" onclick="copyMarkdown()">📋 Copy to Clipboard</button>
</div>
</div>
</div>
</div>
<!-- Export Tab -->
<div id="export" class="tab-content">
<h3>💾 Export & Preview</h3>
<div id="yamlPreview" class="hidden">
<h4>YAML Preview:</h4>
<textarea id="yamlPreviewText" readonly style="width: 100%; height: 400px; font-family: monospace;"></textarea>
</div>
<div style="text-align: center; margin-top: 20px;">
<button class="btn" onclick="previewYAML()">👁️ Preview YAML</button>
<button class="btn btn-success" onclick="exportYAML()">📥 Export YAML</button>
</div>
</div>
</div>
<script>
let yamlData = null;
let currentEditingIndex = -1;
let selectedTools = new Set();
let filteredToolsCache = [];
// Initialize with correct YAML structure
function init() {
yamlData = {
tools: [],
domains: [
{ id: 'incident-response', name: 'Incident Response & Breach-Untersuchung' },
{ id: 'static-investigations', name: 'Datenträgerforensik & Ermittlungen' },
{ id: 'malware-analysis', name: 'Malware-Analyse & Reverse Engineering' },
{ id: 'fraud-investigation', name: 'Betrugs- & Finanzkriminalität' },
{ id: 'network-forensics', name: 'Netzwerk-Forensik & Traffic-Analyse' },
{ id: 'mobile-forensics', name: 'Mobile Geräte & App-Forensik' },
{ id: 'cloud-forensics', name: 'Cloud & Virtuelle Umgebungen' },
{ id: 'ics-forensics', name: 'Industrielle Kontrollsysteme (ICS/SCADA)' }
],
phases: [
{ id: 'data-collection', name: 'Datensammlung', description: 'Imaging, Acquisition, Remote Collection Tools' },
{ id: 'examination', name: 'Auswertung', description: 'Parsing, Extraction, Initial Analysis Tools' },
{ id: 'analysis', name: 'Analyse', description: 'Deep Analysis, Correlation, Visualization Tools' },
{ id: 'reporting', name: 'Bericht & Präsentation', description: 'Documentation, Visualization, Presentation Tools' }
],
'domain-agnostic-software': [
{ id: 'collaboration-general', name: 'Übergreifend & Kollaboration', description: 'Cross-cutting tools and collaboration platforms' },
{ id: 'specific-os', name: 'Betriebssysteme', description: 'Operating Systems which focus on forensics' }
]
};
populateFormOptions();
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
}
function showTab(tabName) {
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
document.getElementById(tabName).classList.add('active');
event.target.classList.add('active');
if (tabName === 'tools') renderToolsGrid();
else if (tabName === 'bulk') renderBulkGrid();
else if (tabName === 'knowledge') updateKnowledgeToolSelect();
}
// Enhanced Search Functionality
function applySearch() {
applyFilters();
}
function searchTools(tools, searchTerm) {
if (!searchTerm) return tools;
const term = searchTerm.toLowerCase();
return tools.filter(tool => {
// Search in name
if (tool.name && tool.name.toLowerCase().includes(term)) return true;
// Search in description
if (tool.description && tool.description.toLowerCase().includes(term)) return true;
// Search in tags
if (tool.tags && tool.tags.some(tag => tag.toLowerCase().includes(term))) return true;
// Search in related concepts
if (tool.related_concepts && tool.related_concepts.some(concept => concept.toLowerCase().includes(term))) return true;
// Search in type
if (tool.type && tool.type.toLowerCase().includes(term)) return true;
// Search in platforms
if (tool.platforms && tool.platforms.some(platform => platform.toLowerCase().includes(term))) return true;
return false;
});
}
function populateFormOptions() {
// Populate domains
const domainsContainer = document.getElementById('domainsCheckbox');
domainsContainer.innerHTML = '';
yamlData.domains.forEach(domain => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `<input type="checkbox" value="${domain.id}" id="domain-${domain.id}"><label for="domain-${domain.id}">${domain.name}</label>`;
domainsContainer.appendChild(div);
});
// Populate phases
const phasesContainer = document.getElementById('phasesCheckbox');
phasesContainer.innerHTML = '';
yamlData.phases.forEach(phase => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `<input type="checkbox" value="${phase.id}" id="phase-${phase.id}"><label for="phase-${phase.id}">${phase.name}</label>`;
phasesContainer.appendChild(div);
});
// Populate domain-agnostic software
const domainAgnosticContainer = document.getElementById('domainAgnosticCheckbox');
domainAgnosticContainer.innerHTML = '';
yamlData['domain-agnostic-software'].forEach(category => {
const div = document.createElement('div');
div.className = 'checkbox-item';
div.innerHTML = `<input type="checkbox" value="${category.id}" id="domain-agnostic-${category.id}"><label for="domain-agnostic-${category.id}">${category.name}</label>`;
domainAgnosticContainer.appendChild(div);
});
}
function toggleConditionalFields() {
const toolType = document.getElementById('toolType').value;
const softwareMethodFields = document.getElementById('softwareMethodFields');
const softwareFields = document.getElementById('softwareFields');
if (toolType === 'concept') {
softwareMethodFields.style.display = 'none';
softwareFields.style.display = 'none';
} else if (toolType === 'method') {
softwareMethodFields.style.display = 'block';
softwareFields.style.display = 'none';
} else {
softwareMethodFields.style.display = 'block';
softwareFields.style.display = 'block';
}
}
function loadFile() {
const file = document.getElementById('fileInput').files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(e) {
try {
yamlData = jsyaml.load(e.target.result);
showMessage('YAML file loaded successfully!', 'success');
populateFormOptions();
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
} catch (error) {
showMessage('Error loading YAML file: ' + error.message, 'error');
}
};
reader.readAsText(file);
}
function showMessage(message, type = 'success') {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
messageDiv.textContent = message;
document.body.appendChild(messageDiv);
setTimeout(() => document.body.removeChild(messageDiv), 3000);
}
function updateStats() {
if (!yamlData?.tools) return;
const stats = {
total: yamlData.tools.length,
software: yamlData.tools.filter(t => t.type === 'software' || !t.type).length,
methods: yamlData.tools.filter(t => t.type === 'method').length,
concepts: yamlData.tools.filter(t => t.type === 'concept').length,
withKnowledgebase: yamlData.tools.filter(t => t.knowledgebase).length
};
document.getElementById('stats').innerHTML = `
<div class="stat-card"><h3>${stats.total}</h3><p>Total Items</p></div>
<div class="stat-card"><h3>${stats.software}</h3><p>Software Tools</p></div>
<div class="stat-card"><h3>${stats.methods}</h3><p>Methods</p></div>
<div class="stat-card"><h3>${stats.concepts}</h3><p>Concepts</p></div>
<div class="stat-card"><h3>${stats.withKnowledgebase}</h3><p>With Knowledgebase</p></div>
`;
}
// Tag input handlers
function focusTagInput() { document.getElementById('tagInputField').focus(); }
function focusRelatedConceptInput() { document.getElementById('relatedConceptInputField').focus(); }
function handleTagInput(event) {
if (event.key === 'Enter' || event.key === ',') {
event.preventDefault();
const input = event.target;
const value = input.value.trim();
if (value) {
addTag('tagsInput', value);
input.value = '';
}
}
}
function handleRelatedConceptInput(event) {
if (event.key === 'Enter' || event.key === ',') {
event.preventDefault();
const input = event.target;
const value = input.value.trim();
if (value) {
addTag('relatedConceptsInput', value);
input.value = '';
}
}
}
function addTag(containerId, value) {
const container = document.getElementById(containerId);
const input = container.querySelector('input');
const tag = document.createElement('span');
tag.className = 'tag';
tag.innerHTML = `${value} <span class="tag-remove" onclick="this.parentElement.remove()">×</span>`;
container.insertBefore(tag, input);
}
function getCheckedValues(selector) {
return Array.from(document.querySelectorAll(selector + ':checked')).map(cb => cb.value);
}
function getTags() {
return Array.from(document.querySelectorAll('#tagsInput .tag')).map(tag =>
tag.textContent.replace('×', '').trim()
).filter(tag => tag);
}
function getRelatedConcepts() {
return Array.from(document.querySelectorAll('#relatedConceptsInput .tag')).map(tag =>
tag.textContent.replace('×', '').trim()
).filter(concept => concept);
}
function saveTool() {
try {
if (!yamlData) yamlData = { tools: [] };
if (!yamlData.tools) yamlData.tools = [];
const toolType = document.getElementById('toolType').value;
const tool = {
name: document.getElementById('toolName').value,
type: toolType,
description: document.getElementById('description').value,
skillLevel: document.getElementById('skillLevel').value,
url: document.getElementById('url').value
};
// Add icon if provided
const icon = document.getElementById('toolIcon').value.trim();
if (icon) tool.icon = icon;
// Add domains and phases
tool.domains = getCheckedValues('#domainsCheckbox input:checked');
tool.phases = getCheckedValues('#phasesCheckbox input:checked');
// Add tags and related concepts
const tags = getTags();
if (tags.length > 0) tool.tags = tags;
const relatedConcepts = getRelatedConcepts();
if (relatedConcepts.length > 0) tool.related_concepts = relatedConcepts;
// Type-specific fields
if (toolType === 'software') {
tool.platforms = getCheckedValues('#platformsCheckbox input:checked');
const accessType = document.getElementById('accessType').value;
if (accessType) tool.accessType = accessType;
const license = document.getElementById('license').value.trim();
if (license) tool.license = license;
const projectUrl = document.getElementById('projectUrl').value.trim();
if (projectUrl) tool.projectUrl = projectUrl;
const statusUrl = document.getElementById('statusUrl').value.trim();
if (statusUrl) tool.statusUrl = statusUrl;
tool.knowledgebase = document.getElementById('knowledgebase').checked;
const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked');
if (domainAgnostic.length > 0) tool['domain-agnostic-software'] = domainAgnostic;
} else if (toolType === 'method') {
tool.platforms = getCheckedValues('#platformsCheckbox input:checked');
const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked');
if (domainAgnostic.length > 0) tool['domain-agnostic-software'] = domainAgnostic;
tool.knowledgebase = document.getElementById('knowledgebase').checked;
} else if (toolType === 'concept') {
tool.knowledgebase = document.getElementById('knowledgebase').checked;
}
if (currentEditingIndex >= 0) {
yamlData.tools[currentEditingIndex] = tool;
showMessage('Tool updated successfully!');
currentEditingIndex = -1;
} else {
yamlData.tools.push(tool);
showMessage('Tool added successfully!');
}
clearForm();
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
} catch (error) {
showMessage('Error saving tool: ' + error.message, 'error');
}
}
function clearForm() {
document.getElementById('toolForm').reset();
document.getElementById('tagsInput').innerHTML = '<input type="text" id="tagInputField" placeholder="Add tags..." onkeydown="handleTagInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
document.getElementById('relatedConceptsInput').innerHTML = '<input type="text" id="relatedConceptInputField" placeholder="Add concept names..." onkeydown="handleRelatedConceptInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
currentEditingIndex = -1;
toggleConditionalFields();
}
function cancelEdit() {
clearForm();
showMessage('Edit cancelled');
}
function editTool(index) {
const tool = yamlData.tools[index];
currentEditingIndex = index;
// Populate form fields
document.getElementById('toolType').value = tool.type || 'software';
document.getElementById('toolName').value = tool.name || '';
document.getElementById('toolIcon').value = tool.icon || '';
document.getElementById('description').value = tool.description || '';
document.getElementById('skillLevel').value = tool.skillLevel || 'intermediate';
document.getElementById('url').value = tool.url || '';
// Software-specific fields
if (tool.type === 'software' || !tool.type) {
document.getElementById('accessType').value = tool.accessType || '';
document.getElementById('projectUrl').value = tool.projectUrl || '';
document.getElementById('license').value = tool.license || '';
document.getElementById('statusUrl').value = tool.statusUrl || '';
}
document.getElementById('knowledgebase').checked = tool.knowledgebase || false;
// Set checkboxes
setCheckboxValues('#domainsCheckbox input', tool.domains || []);
setCheckboxValues('#phasesCheckbox input', tool.phases || []);
setCheckboxValues('#platformsCheckbox input', tool.platforms || []);
setCheckboxValues('#domainAgnosticCheckbox input', tool['domain-agnostic-software'] || []);
// Set tags
const tagsContainer = document.getElementById('tagsInput');
tagsContainer.innerHTML = '<input type="text" id="tagInputField" placeholder="Add tags..." onkeydown="handleTagInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
(tool.tags || []).forEach(tag => addTag('tagsInput', tag));
// Set related concepts
const conceptsContainer = document.getElementById('relatedConceptsInput');
conceptsContainer.innerHTML = '<input type="text" id="relatedConceptInputField" placeholder="Add concept names..." onkeydown="handleRelatedConceptInput(event)" style="border: none; outline: none; flex: 1; min-width: 100px;">';
(tool.related_concepts || []).forEach(concept => addTag('relatedConceptsInput', concept));
toggleConditionalFields();
showTab('editor');
}
function setCheckboxValues(selector, values) {
document.querySelectorAll(selector).forEach(cb => {
cb.checked = values.includes(cb.value);
});
}
function deleteTool(index) {
if (confirm('Are you sure you want to delete this tool?')) {
yamlData.tools.splice(index, 1);
showMessage('Tool deleted successfully!');
updateStats();
renderToolsGrid();
updateKnowledgeToolSelect();
}
}
function renderToolsGrid() {
const container = document.getElementById('toolsGrid');
container.innerHTML = '';
if (!yamlData?.tools) return;
let filteredTools = yamlData.tools;
// Apply search filter
const searchTerm = document.getElementById('searchInput')?.value;
if (searchTerm) {
filteredTools = searchTools(filteredTools, searchTerm);
}
// Apply type and skill filters
const typeFilter = document.getElementById('typeFilter')?.value;
const skillFilter = document.getElementById('skillFilter')?.value;
if (typeFilter) filteredTools = filteredTools.filter(tool => (tool.type || 'software') === typeFilter);
if (skillFilter) filteredTools = filteredTools.filter(tool => tool.skillLevel === skillFilter);
// Cache filtered results
filteredToolsCache = filteredTools;
// Update search results info
const searchResults = document.getElementById('searchResults');
if (searchResults) {
const totalTools = yamlData.tools.length;
const filteredCount = filteredTools.length;
if (searchTerm || typeFilter || skillFilter) {
searchResults.textContent = `Showing ${filteredCount} of ${totalTools} tools`;
} else {
searchResults.textContent = `Showing all ${totalTools} tools`;
}
}
filteredTools.forEach((tool, index) => {
const originalIndex = yamlData.tools.indexOf(tool);
const card = createToolCard(tool, originalIndex);
container.appendChild(card);
});
}
function createToolCard(tool, index) {
const card = document.createElement('div');
card.className = `tool-card ${tool.type || 'software'}`;
const tags = (tool.tags || []).map(tag => `<span class="tag">${tag}</span>`).join('');
const knowledgebaseIndicator = tool.knowledgebase ? '<span class="tag" style="background: #e8f5e8; color: #27ae60;">📚 KB</span>' : '';
card.innerHTML = `
<h3>${tool.icon ? tool.icon + ' ' : ''}${tool.name} <span style="font-size: 0.7em; color: #666;">[${tool.type || 'software'}]</span></h3>
<p>${tool.description}</p>
<div style="margin: 10px 0;">${tags} ${knowledgebaseIndicator}</div>
<div style="display: flex; gap: 5px;">
<button class="btn btn-secondary" onclick="editTool(${index})" style="flex: 1; padding: 8px;">✏️ Edit</button>
<button class="btn btn-danger" onclick="deleteTool(${index})" style="flex: 1; padding: 8px;">🗑️ Delete</button>
</div>
`;
return card;
}
function applyFilters() { renderToolsGrid(); }
function clearFilters() {
document.getElementById('typeFilter').value = '';
document.getElementById('skillFilter').value = '';
document.getElementById('searchInput').value = '';
renderToolsGrid();
}
// Enhanced Bulk Operations
function renderBulkGrid() {
const container = document.getElementById('bulkToolsGrid');
container.innerHTML = '';
if (!yamlData?.tools) return;
yamlData.tools.forEach((tool, index) => {
const card = createBulkToolCard(tool, index);
container.appendChild(card);
});
updateSelectionCount();
}
function createBulkToolCard(tool, index) {
const card = document.createElement('div');
card.className = `tool-card ${tool.type || 'software'}`;
const isSelected = selectedTools.has(index);
card.style.opacity = isSelected ? '1' : '0.7';
card.style.border = isSelected ? '2px solid var(--primary)' : '1px solid var(--border)';
card.innerHTML = `
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
<input type="checkbox" ${isSelected ? 'checked' : ''} onchange="toggleToolSelection(${index})" style="transform: scale(1.2);">
<h3 style="margin: 0; flex: 1;">${tool.icon ? tool.icon + ' ' : ''}${tool.name}</h3>
</div>
<p>${tool.description}</p>
<div><span style="background: var(--light); padding: 2px 6px; border-radius: 10px; font-size: 0.8em;">${tool.type || 'software'}</span></div>
`;
return card;
}
function toggleToolSelection(index) {
if (selectedTools.has(index)) {
selectedTools.delete(index);
} else {
selectedTools.add(index);
}
updateSelectionCount();
renderBulkGrid();
}
function selectAllTools() {
selectedTools.clear();
yamlData.tools.forEach((_, index) => selectedTools.add(index));
updateSelectionCount();
renderBulkGrid();
}
function clearSelection() {
selectedTools.clear();
updateSelectionCount();
renderBulkGrid();
}
function updateSelectionCount() {
const count = selectedTools.size;
document.getElementById('selectionInfo').textContent =
count === 0 ? 'No tools selected' : `${count} tool(s) selected`;
}
// Enhanced bulk operations with new clear functions
function bulkSetType() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const newType = prompt('Enter new type (software/method/concept):');
if (newType && ['software', 'method', 'concept'].includes(newType)) {
selectedTools.forEach(index => yamlData.tools[index].type = newType);
showMessage(`Updated type for ${selectedTools.size} tools`);
updateStats();
renderBulkGrid();
}
}
function bulkSetSkillLevel() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const newLevel = prompt('Enter skill level (novice/beginner/intermediate/advanced/expert):');
if (newLevel && ['novice', 'beginner', 'intermediate', 'advanced', 'expert'].includes(newLevel)) {
selectedTools.forEach(index => yamlData.tools[index].skillLevel = newLevel);
showMessage(`Updated skill level for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkAddTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const tags = prompt('Enter tags to add (comma-separated):');
if (tags) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
tool.tags = [...new Set([...(tool.tags || []), ...tagList])];
});
showMessage(`Added tags to ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkRemoveTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const tags = prompt('Enter tags to remove (comma-separated):');
if (tags) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
if (tool.tags) {
tool.tags = tool.tags.filter(tag => !tagList.includes(tag));
if (tool.tags.length === 0) delete tool.tags;
}
});
showMessage(`Removed tags from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkReplaceTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const tags = prompt('Enter new tags (comma-separated, will replace all existing tags):');
if (tags !== null) {
const tagList = tags.split(',').map(t => t.trim()).filter(t => t);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
if (tagList.length > 0) {
tool.tags = tagList;
} else {
delete tool.tags;
}
});
showMessage(`Replaced tags for ${selectedTools.size} tools`);
renderBulkGrid();
}
}
// NEW: Clear field functions
function bulkClearTags() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL tags from ${selectedTools.size} selected tools?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].tags;
});
showMessage(`Cleared tags from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkAddDomains() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const domains = prompt('Enter domain IDs to add (comma-separated):');
if (domains) {
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
tool.domains = [...new Set([...(tool.domains || []), ...domainList])];
});
showMessage(`Added domains to ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkRemoveDomains() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const domains = prompt('Enter domain IDs to remove (comma-separated):');
if (domains) {
const domainList = domains.split(',').map(d => d.trim()).filter(d => d);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
if (tool.domains) {
tool.domains = tool.domains.filter(domain => !domainList.includes(domain));
if (tool.domains.length === 0) delete tool.domains;
}
});
showMessage(`Removed domains from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkClearDomains() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL domains from ${selectedTools.size} selected tools?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].domains;
});
showMessage(`Cleared domains from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkAddPhases() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const phases = prompt('Enter phase IDs to add (comma-separated):');
if (phases) {
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
tool.phases = [...new Set([...(tool.phases || []), ...phaseList])];
});
showMessage(`Added phases to ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkRemovePhases() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const phases = prompt('Enter phase IDs to remove (comma-separated):');
if (phases) {
const phaseList = phases.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
if (tool.phases) {
tool.phases = tool.phases.filter(phase => !phaseList.includes(phase));
if (tool.phases.length === 0) delete tool.phases;
}
});
showMessage(`Removed phases from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkClearPhases() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL phases from ${selectedTools.size} selected tools?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].phases;
});
showMessage(`Cleared phases from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
// NEW: Platform operations
function bulkAddPlatforms() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const platforms = prompt('Enter platforms to add (comma-separated, e.g., Windows,Linux,macOS):');
if (platforms) {
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
tool.platforms = [...new Set([...(tool.platforms || []), ...platformList])];
});
showMessage(`Added platforms to ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkRemovePlatforms() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const platforms = prompt('Enter platforms to remove (comma-separated):');
if (platforms) {
const platformList = platforms.split(',').map(p => p.trim()).filter(p => p);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
if (tool.platforms) {
tool.platforms = tool.platforms.filter(platform => !platformList.includes(platform));
if (tool.platforms.length === 0) delete tool.platforms;
}
});
showMessage(`Removed platforms from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkClearPlatforms() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL platforms from ${selectedTools.size} selected tools?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].platforms;
});
showMessage(`Cleared platforms from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkAddRelatedConcepts() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const concepts = prompt('Enter related concept names to add (comma-separated):');
if (concepts) {
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
tool.related_concepts = [...new Set([...(tool.related_concepts || []), ...conceptList])];
});
showMessage(`Added related concepts to ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkRemoveRelatedConcepts() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
const concepts = prompt('Enter related concept names to remove (comma-separated):');
if (concepts) {
const conceptList = concepts.split(',').map(c => c.trim()).filter(c => c);
selectedTools.forEach(index => {
const tool = yamlData.tools[index];
if (tool.related_concepts) {
tool.related_concepts = tool.related_concepts.filter(concept => !conceptList.includes(concept));
if (tool.related_concepts.length === 0) delete tool.related_concepts;
}
});
showMessage(`Removed related concepts from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkClearRelatedConcepts() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to clear ALL related concepts from ${selectedTools.size} selected tools?`)) {
selectedTools.forEach(index => {
delete yamlData.tools[index].related_concepts;
});
showMessage(`Cleared related concepts from ${selectedTools.size} tools`);
renderBulkGrid();
}
}
function bulkDelete() {
if (selectedTools.size === 0) return showMessage('No tools selected', 'error');
if (confirm(`Are you sure you want to delete ${selectedTools.size} selected tools?`)) {
const indicesToDelete = Array.from(selectedTools).sort((a, b) => b - a);
indicesToDelete.forEach(index => yamlData.tools.splice(index, 1));
selectedTools.clear();
showMessage(`Deleted ${indicesToDelete.length} tools successfully!`);
updateStats();
renderBulkGrid();
updateKnowledgeToolSelect();
}
}
// Knowledge Generator - Enhanced for CC24-Hub format
function updateKnowledgeToolSelect() {
const select = document.getElementById('knowledgeToolSelect');
select.innerHTML = '<option value="">Choose a tool or concept...</option>';
if (!yamlData?.tools) return;
yamlData.tools.forEach((tool, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${tool.icon ? tool.icon + ' ' : ''}${tool.name} (${tool.type || 'software'})`;
select.appendChild(option);
});
}
function generateKnowledgeTemplate() {
const select = document.getElementById('knowledgeToolSelect');
const index = parseInt(select.value);
if (isNaN(index)) {
document.getElementById('knowledgePreview').classList.add('hidden');
return;
}
const tool = yamlData.tools[index];
const template = createCC24MarkdownTemplate(tool);
document.getElementById('markdownContent').value = template;
document.getElementById('knowledgePreview').classList.remove('hidden');
}
function createCC24MarkdownTemplate(tool) {
const toolSlug = tool.name.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
return `---
title: "${tool.name}"
description: "${tool.description.split('\n')[0].trim()}"
last_updated: ${new Date().toISOString().split('T')[0]}
tool_name: "${tool.name}"
related_tools: ${tool.related_concepts ? JSON.stringify(tool.related_concepts) : '[]'}
author: "CC24-Team"
difficulty: "${tool.skillLevel || 'intermediate'}"
categories: ${tool.type === 'concept' ? '["concepts"]' : tool.type === 'method' ? '["methods"]' : '["tools"]'}
tags: ${tool.tags ? JSON.stringify(tool.tags) : '[]'}
published: true
---
# ${tool.icon ? tool.icon + ' ' : ''}${tool.name}
## Übersicht
${tool.description}
**Typ**: ${tool.type || 'software'}
**Skill Level**: ${tool.skillLevel || 'intermediate'}
**Offizielle URL**: [${tool.name}](${tool.url})
${tool.license ? `**Lizenz**: ${tool.license}\n` : ''}${tool.platforms && tool.platforms.length > 0 ? `**Plattformen**: ${tool.platforms.join(', ')}\n` : ''}${tool.accessType ? `**Zugriff**: ${tool.accessType}\n` : ''}
${tool.domains && tool.domains.length > 0 ? `## Anwendungsbereiche
${tool.domains.map(domain => `- ${domain}`).join('\n')}\n\n` : ''}${tool.phases && tool.phases.length > 0 ? `## Ermittlungsphasen
${tool.phases.map(phase => `- ${phase}`).join('\n')}\n\n` : ''}## ${tool.type === 'concept' ? 'Grundlagen' : tool.type === 'method' ? 'Vorgehensweise' : 'Installation & Nutzung'}
${tool.type === 'concept' ?
`### Kernkonzepte
TODO: Beschreibe die wichtigsten Konzepte und Prinzipien.
### Anwendungsbereiche
TODO: Erkläre, wo und wie dieses Konzept angewendet wird.` :
tool.type === 'method' ?
`### Schritt-für-Schritt Anleitung
1. TODO: Erster Schritt
2. TODO: Zweiter Schritt
3. TODO: Dritter Schritt
### Voraussetzungen
TODO: Liste die erforderlichen Voraussetzungen auf.` :
`### Installation
TODO: Beschreibe die Installation für die relevanten Plattformen.
### Grundlegende Nutzung
TODO: Erkläre die wichtigsten Funktionen und Befehle.
### Workflow-Beispiele
TODO: Zeige typische Anwendungsfälle und Workflows.`}
## Best Practices
TODO: Teile bewährte Praktiken und Tipps für die optimale Nutzung.
## Häufige Probleme
TODO: Beschreibe häufige Stolpersteine und deren Lösungen.
${tool.related_concepts && tool.related_concepts.length > 0 ? `## Verwandte Tools und Konzepte
${tool.related_concepts.map(concept => `- ${concept}`).join('\n')}\n\n` : ''}## Weitere Ressourcen
- [Offizielle Dokumentation](${tool.url})${tool.projectUrl ? `\n- [CC24 Server Zugang](${tool.projectUrl})` : ''}
TODO: Füge weitere nützliche Links und Ressourcen hinzu.
---
*Zuletzt aktualisiert: ${new Date().toLocaleDateString('de-DE')}*
`;
}
function downloadMarkdown() {
const content = document.getElementById('markdownContent').value;
const select = document.getElementById('knowledgeToolSelect');
const index = parseInt(select.value);
const tool = yamlData.tools[index];
const toolSlug = tool.name.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
const blob = new Blob([content], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${toolSlug}.md`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showMessage('Markdown template downloaded successfully!');
}
function copyMarkdown() {
const content = document.getElementById('markdownContent').value;
navigator.clipboard.writeText(content).then(() => {
showMessage('Markdown copied to clipboard!');
}).catch(() => {
showMessage('Failed to copy to clipboard', 'error');
});
}
// Validation and Export
function validateYAML() {
if (!yamlData) return showMessage('No data to validate', 'error');
const validationResults = [];
// Check required sections
if (!yamlData.tools) validationResults.push('❌ Missing tools section');
if (!yamlData.domains) validationResults.push('❌ Missing domains section');
if (!yamlData.phases) validationResults.push('❌ Missing phases section');
// Validate tools
yamlData.tools?.forEach((tool, index) => {
if (!tool.name) validationResults.push(`❌ Tool ${index + 1}: Missing name`);
if (!tool.description) validationResults.push(`❌ Tool ${index + 1}: Missing description`);
if (!tool.skillLevel) validationResults.push(`❌ Tool ${index + 1}: Missing skillLevel`);
if (!tool.url) validationResults.push(`❌ Tool ${index + 1}: Missing url`);
if (tool.type && !['software', 'method', 'concept'].includes(tool.type)) {
validationResults.push(`❌ Tool ${index + 1}: Invalid type`);
}
// Type-specific validation
if (tool.type === 'software' && (!tool.platforms || tool.platforms.length === 0)) {
validationResults.push(`⚠️ Tool ${index + 1}: Software should have platforms`);
}
if (tool.type === 'concept' && tool.platforms?.length > 0) {
validationResults.push(`⚠️ Tool ${index + 1}: Concepts should not have platforms`);
}
});
const container = document.getElementById('validationContent');
if (validationResults.length === 0) {
container.innerHTML = '<div class="message success">✅ YAML structure is valid!</div>';
} else {
container.innerHTML = '<div class="message error">' + validationResults.join('<br>') + '</div>';
}
document.getElementById('validationResults').classList.remove('hidden');
}
function previewYAML() {
if (!yamlData) return showMessage('No data to preview', 'error');
const yamlString = jsyaml.dump(yamlData, { indent: 2 });
document.getElementById('yamlPreviewText').value = yamlString;
document.getElementById('yamlPreview').classList.remove('hidden');
}
function exportYAML() {
if (!yamlData) return showMessage('No data to export', 'error');
const yamlString = jsyaml.dump(yamlData, { indent: 2 });
const blob = new Blob([yamlString], { type: 'text/yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'tools.yaml';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showMessage('YAML file exported successfully!');
}
// Initialize
init();
</script>
</body>
</html>