1907 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			1907 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						||
<html lang="en">
 | 
						||
<head>
 | 
						||
    <meta charset="UTF-8">
 | 
						||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
						||
    <title>Tools.yaml Editor</title>
 | 
						||
    <script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"></script>
 | 
						||
    <style>
 | 
						||
        :root {
 | 
						||
            --bg-primary: #ffffff;
 | 
						||
            --bg-secondary: #f8f9fa;
 | 
						||
            --bg-tertiary: #e9ecef;
 | 
						||
            --text-primary: #212529;
 | 
						||
            --text-secondary: #6c757d;
 | 
						||
            --border-color: #dee2e6;
 | 
						||
            --accent-color: #0d6efd;
 | 
						||
            --success-color: #198754;
 | 
						||
            --warning-color: #fd7e14;
 | 
						||
            --danger-color: #dc3545;
 | 
						||
            --shadow: 0 2px 4px rgba(0,0,0,0.1);
 | 
						||
        }
 | 
						||
 | 
						||
        [data-theme="dark"] {
 | 
						||
            --bg-primary: #1a1a1a;
 | 
						||
            --bg-secondary: #2d2d2d;
 | 
						||
            --bg-tertiary: #404040;
 | 
						||
            --text-primary: #ffffff;
 | 
						||
            --text-secondary: #b0b0b0;
 | 
						||
            --border-color: #404040;
 | 
						||
            --accent-color: #4a9eff;
 | 
						||
            --success-color: #28a745;
 | 
						||
            --warning-color: #ffc107;
 | 
						||
            --danger-color: #dc3545;
 | 
						||
            --shadow: 0 2px 4px rgba(0,0,0,0.3);
 | 
						||
        }
 | 
						||
 | 
						||
        * {
 | 
						||
            margin: 0;
 | 
						||
            padding: 0;
 | 
						||
            box-sizing: border-box;
 | 
						||
        }
 | 
						||
 | 
						||
        body {
 | 
						||
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 | 
						||
            background-color: var(--bg-primary);
 | 
						||
            color: var(--text-primary);
 | 
						||
            line-height: 1.5;
 | 
						||
        }
 | 
						||
 | 
						||
        .container {
 | 
						||
            max-width: 1400px;
 | 
						||
            margin: 0 auto;
 | 
						||
            padding: 20px;
 | 
						||
        }
 | 
						||
 | 
						||
        .header {
 | 
						||
            display: flex;
 | 
						||
            justify-content: space-between;
 | 
						||
            align-items: center;
 | 
						||
            margin-bottom: 20px;
 | 
						||
            padding: 20px;
 | 
						||
            background: var(--bg-secondary);
 | 
						||
            border-radius: 8px;
 | 
						||
            box-shadow: var(--shadow);
 | 
						||
        }
 | 
						||
 | 
						||
        .controls {
 | 
						||
            display: flex;
 | 
						||
            gap: 15px;
 | 
						||
            align-items: center;
 | 
						||
        }
 | 
						||
 | 
						||
        .search-section {
 | 
						||
            display: grid;
 | 
						||
            grid-template-columns: 1fr auto;
 | 
						||
            gap: 20px;
 | 
						||
            margin-bottom: 20px;
 | 
						||
            padding: 20px;
 | 
						||
            background: var(--bg-secondary);
 | 
						||
            border-radius: 8px;
 | 
						||
            box-shadow: var(--shadow);
 | 
						||
        }
 | 
						||
 | 
						||
        .search-filters {
 | 
						||
            display: grid;
 | 
						||
            grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1fr 1fr;
 | 
						||
            gap: 15px;
 | 
						||
            align-items: end;
 | 
						||
        }
 | 
						||
 | 
						||
        .bulk-actions {
 | 
						||
            display: flex;
 | 
						||
            gap: 10px;
 | 
						||
        }
 | 
						||
 | 
						||
        .main-content {
 | 
						||
            display: grid;
 | 
						||
            grid-template-columns: 1fr 400px;
 | 
						||
            gap: 20px;
 | 
						||
        }
 | 
						||
 | 
						||
        .tools-section {
 | 
						||
            background: var(--bg-secondary);
 | 
						||
            border-radius: 8px;
 | 
						||
            padding: 20px;
 | 
						||
            box-shadow: var(--shadow);
 | 
						||
        }
 | 
						||
 | 
						||
        .edit-section {
 | 
						||
            background: var(--bg-secondary);
 | 
						||
            border-radius: 8px;
 | 
						||
            padding: 20px;
 | 
						||
            box-shadow: var(--shadow);
 | 
						||
            position: sticky;
 | 
						||
            top: 20px;
 | 
						||
            max-height: calc(100vh - 40px);
 | 
						||
            overflow-y: auto;
 | 
						||
        }
 | 
						||
 | 
						||
        .tools-header {
 | 
						||
            display: flex;
 | 
						||
            justify-content: space-between;
 | 
						||
            align-items: center;
 | 
						||
            margin-bottom: 15px;
 | 
						||
        }
 | 
						||
 | 
						||
        .tools-list {
 | 
						||
            max-height: 70vh;
 | 
						||
            overflow-y: auto;
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-item {
 | 
						||
            padding: 15px;
 | 
						||
            border: 1px solid var(--border-color);
 | 
						||
            border-radius: 6px;
 | 
						||
            margin-bottom: 10px;
 | 
						||
            cursor: pointer;
 | 
						||
            transition: all 0.2s;
 | 
						||
            background: var(--bg-primary);
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-item:hover {
 | 
						||
            background: var(--bg-tertiary);
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-item.selected {
 | 
						||
            border-color: var(--accent-color);
 | 
						||
            background: var(--bg-tertiary);
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-header {
 | 
						||
            display: flex;
 | 
						||
            justify-content: space-between;
 | 
						||
            align-items: flex-start;
 | 
						||
            margin-bottom: 8px;
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-name {
 | 
						||
            font-weight: 600;
 | 
						||
            color: var(--accent-color);
 | 
						||
            margin-bottom: 4px;
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-meta {
 | 
						||
            display: flex;
 | 
						||
            gap: 10px;
 | 
						||
            font-size: 0.85em;
 | 
						||
            color: var(--text-secondary);
 | 
						||
        }
 | 
						||
 | 
						||
        .tool-description {
 | 
						||
            font-size: 0.9em;
 | 
						||
            margin-top: 8px;
 | 
						||
            display: -webkit-box;
 | 
						||
            -webkit-box-orient: vertical;
 | 
						||
            overflow: hidden;
 | 
						||
        }
 | 
						||
 | 
						||
        .tags {
 | 
						||
            display: flex;
 | 
						||
            flex-wrap: wrap;
 | 
						||
            gap: 4px;
 | 
						||
            margin-top: 8px;
 | 
						||
        }
 | 
						||
 | 
						||
        .tag {
 | 
						||
            background: var(--accent-color);
 | 
						||
            color: white;
 | 
						||
            padding: 2px 8px;
 | 
						||
            border-radius: 12px;
 | 
						||
            font-size: 0.75em;
 | 
						||
        }
 | 
						||
 | 
						||
        .form-group {
 | 
						||
            margin-bottom: 15px;
 | 
						||
        }
 | 
						||
 | 
						||
        .form-label {
 | 
						||
            display: block;
 | 
						||
            margin-bottom: 5px;
 | 
						||
            font-weight: 500;
 | 
						||
        }
 | 
						||
 | 
						||
        .form-input, .form-select, .form-textarea {
 | 
						||
            width: 100%;
 | 
						||
            padding: 8px 12px;
 | 
						||
            border: 1px solid var(--border-color);
 | 
						||
            border-radius: 4px;
 | 
						||
            background: var(--bg-primary);
 | 
						||
            color: var(--text-primary);
 | 
						||
            font-size: 14px;
 | 
						||
        }
 | 
						||
 | 
						||
        .form-textarea {
 | 
						||
            resize: vertical;
 | 
						||
            min-height: 80px;
 | 
						||
        }
 | 
						||
 | 
						||
        .form-checkbox {
 | 
						||
            margin-right: 8px;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn {
 | 
						||
            padding: 8px 16px;
 | 
						||
            border: none;
 | 
						||
            border-radius: 4px;
 | 
						||
            cursor: pointer;
 | 
						||
            font-size: 14px;
 | 
						||
            transition: all 0.2s;
 | 
						||
            text-decoration: none;
 | 
						||
            display: inline-block;
 | 
						||
            text-align: center;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn-primary {
 | 
						||
            background: var(--accent-color);
 | 
						||
            color: white;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn-secondary {
 | 
						||
            background: var(--bg-tertiary);
 | 
						||
            color: var(--text-primary);
 | 
						||
            border: 1px solid var(--border-color);
 | 
						||
        }
 | 
						||
 | 
						||
        .btn-success {
 | 
						||
            background: var(--success-color);
 | 
						||
            color: white;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn-warning {
 | 
						||
            background: var(--warning-color);
 | 
						||
            color: white;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn-danger {
 | 
						||
            background: var(--danger-color);
 | 
						||
            color: white;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn-sm {
 | 
						||
            padding: 4px 8px;
 | 
						||
            font-size: 12px;
 | 
						||
        }
 | 
						||
 | 
						||
        .btn:hover {
 | 
						||
            opacity: 0.9;
 | 
						||
            transform: translateY(-1px);
 | 
						||
        }
 | 
						||
 | 
						||
        .autocomplete-container {
 | 
						||
            position: relative;
 | 
						||
        }
 | 
						||
 | 
						||
        .autocomplete-dropdown {
 | 
						||
            position: absolute;
 | 
						||
            top: 100%;
 | 
						||
            left: 0;
 | 
						||
            right: 0;
 | 
						||
            background: var(--bg-primary);
 | 
						||
            border: 1px solid var(--border-color);
 | 
						||
            border-top: none;
 | 
						||
            border-radius: 0 0 4px 4px;
 | 
						||
            max-height: 200px;
 | 
						||
            overflow-y: auto;
 | 
						||
            z-index: 1000;
 | 
						||
            display: none;
 | 
						||
        }
 | 
						||
 | 
						||
        .autocomplete-item {
 | 
						||
            padding: 8px 12px;
 | 
						||
            cursor: pointer;
 | 
						||
            border-bottom: 1px solid var(--border-color);
 | 
						||
        }
 | 
						||
 | 
						||
        .autocomplete-item:hover,
 | 
						||
        .autocomplete-item.selected {
 | 
						||
            background: var(--bg-tertiary);
 | 
						||
        }
 | 
						||
 | 
						||
        .autocomplete-item:last-child {
 | 
						||
            border-bottom: none;
 | 
						||
        }
 | 
						||
 | 
						||
        .status-bar {
 | 
						||
            position: fixed;
 | 
						||
            bottom: 0;
 | 
						||
            left: 0;
 | 
						||
            right: 0;
 | 
						||
            background: var(--bg-secondary);
 | 
						||
            border-top: 1px solid var(--border-color);
 | 
						||
            padding: 10px 20px;
 | 
						||
            display: flex;
 | 
						||
            justify-content: space-between;
 | 
						||
            align-items: center;
 | 
						||
            font-size: 0.9em;
 | 
						||
        }
 | 
						||
 | 
						||
        .theme-toggle {
 | 
						||
            background: none;
 | 
						||
            border: 1px solid var(--border-color);
 | 
						||
            color: var(--text-primary);
 | 
						||
            border-radius: 4px;
 | 
						||
            cursor: pointer;
 | 
						||
            padding: 6px 10px;
 | 
						||
        }
 | 
						||
 | 
						||
        .validation-errors {
 | 
						||
            background: var(--danger-color);
 | 
						||
            color: white;
 | 
						||
            padding: 10px;
 | 
						||
            border-radius: 4px;
 | 
						||
            margin-bottom: 15px;
 | 
						||
            display: none;
 | 
						||
        }
 | 
						||
 | 
						||
        .stats {
 | 
						||
            display: grid;
 | 
						||
            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
 | 
						||
            gap: 10px;
 | 
						||
            margin-bottom: 20px;
 | 
						||
        }
 | 
						||
 | 
						||
        .stat-item {
 | 
						||
            text-align: center;
 | 
						||
            padding: 10px;
 | 
						||
            background: var(--bg-tertiary);
 | 
						||
            border-radius: 4px;
 | 
						||
        }
 | 
						||
 | 
						||
        .stat-number {
 | 
						||
            font-size: 1.5em;
 | 
						||
            font-weight: bold;
 | 
						||
            color: var(--accent-color);
 | 
						||
        }
 | 
						||
 | 
						||
        .bulk-panel {
 | 
						||
            display: none;
 | 
						||
            background: var(--bg-tertiary);
 | 
						||
            padding: 15px;
 | 
						||
            border-radius: 6px;
 | 
						||
            margin-top: 15px;
 | 
						||
        }
 | 
						||
 | 
						||
        .bulk-panel.active {
 | 
						||
            display: block;
 | 
						||
        }
 | 
						||
 | 
						||
        .tag-input-container {
 | 
						||
            position: relative;
 | 
						||
        }
 | 
						||
 | 
						||
        .tag-input {
 | 
						||
            display: flex;
 | 
						||
            flex-wrap: wrap;
 | 
						||
            gap: 4px;
 | 
						||
            min-height: 36px;
 | 
						||
            padding: 4px 8px;
 | 
						||
            border: 1px solid var(--border-color);
 | 
						||
            border-radius: 4px;
 | 
						||
            background: var(--bg-primary);
 | 
						||
            cursor: text;
 | 
						||
        }
 | 
						||
 | 
						||
        .tag-input-item {
 | 
						||
            background: var(--accent-color);
 | 
						||
            color: white;
 | 
						||
            padding: 2px 8px;
 | 
						||
            border-radius: 12px;
 | 
						||
            font-size: 0.8em;
 | 
						||
            display: flex;
 | 
						||
            align-items: center;
 | 
						||
            gap: 4px;
 | 
						||
        }
 | 
						||
 | 
						||
        .tag-input-item .remove {
 | 
						||
            cursor: pointer;
 | 
						||
            font-weight: bold;
 | 
						||
        }
 | 
						||
 | 
						||
        .tag-input input {
 | 
						||
            border: none;
 | 
						||
            background: none;
 | 
						||
            outline: none;
 | 
						||
            color: var(--text-primary);
 | 
						||
            min-width: 100px;
 | 
						||
            flex: 1;
 | 
						||
        }
 | 
						||
 | 
						||
        @media (max-width: 1200px) {
 | 
						||
            .main-content {
 | 
						||
                grid-template-columns: 1fr;
 | 
						||
            }
 | 
						||
            
 | 
						||
            .edit-section {
 | 
						||
                position: static;
 | 
						||
                max-height: none;
 | 
						||
            }
 | 
						||
 | 
						||
            .search-filters {
 | 
						||
                grid-template-columns: 1fr;
 | 
						||
            }
 | 
						||
        }
 | 
						||
    </style>
 | 
						||
</head>
 | 
						||
<body data-theme="dark">
 | 
						||
    <div class="container">
 | 
						||
        <header class="header">
 | 
						||
            <h1>🔧 Tools.yaml Editor</h1>
 | 
						||
            <div class="controls">
 | 
						||
                <button class="theme-toggle" onclick="toggleTheme()">☀️</button>
 | 
						||
                <input type="file" id="importFile" accept=".yaml,.yml" style="display: none;" onchange="importYAML(event)">
 | 
						||
                <button class="btn btn-secondary" onclick="document.getElementById('importFile').click()">Import YAML</button>
 | 
						||
                <button class="btn btn-primary" onclick="exportYAML()">Export YAML</button>
 | 
						||
                <button class="btn btn-success" onclick="addNewTool()">Add Tool</button>
 | 
						||
            </div>
 | 
						||
        </header>
 | 
						||
 | 
						||
        <section class="search-section">
 | 
						||
            <div class="search-filters">
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Search Tools</label>
 | 
						||
                    <div class="autocomplete-container">
 | 
						||
                        <input type="text" id="searchInput" class="form-input" placeholder="Search by name, description, tags..." oninput="filterTools()">
 | 
						||
                        <div class="autocomplete-dropdown" id="searchAutocomplete"></div>
 | 
						||
                    </div>
 | 
						||
                </div>
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Type</label>
 | 
						||
                    <select id="typeFilter" class="form-select" onchange="filterTools()">
 | 
						||
                        <option value="">All Types</option>
 | 
						||
                        <option value="software">Software</option>
 | 
						||
                        <option value="method">Method</option>
 | 
						||
                        <option value="concept">Concept</option>
 | 
						||
                    </select>
 | 
						||
                </div>
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Skill Level</label>
 | 
						||
                    <select id="skillFilter" class="form-select" onchange="filterTools()">
 | 
						||
                        <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>
 | 
						||
                </div>
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Domain</label>
 | 
						||
                    <select id="domainFilter" class="form-select" onchange="filterTools()">
 | 
						||
                        <option value="">All Domains</option>
 | 
						||
                    </select>
 | 
						||
                </div>
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Knowledgebase</label>
 | 
						||
                    <select id="knowledgebaseFilter" class="form-select" onchange="filterTools()">
 | 
						||
                        <option value="">All</option>
 | 
						||
                        <option value="true">Has KB</option>
 | 
						||
                        <option value="false">No KB</option>
 | 
						||
                    </select>
 | 
						||
                </div>
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Validation</label>
 | 
						||
                    <select id="validationFilter" class="form-select" onchange="filterTools()">
 | 
						||
                        <option value="">All</option>
 | 
						||
                        <option value="valid">Valid Only</option>
 | 
						||
                        <option value="invalid">Has Issues</option>
 | 
						||
                    </select>
 | 
						||
                </div>
 | 
						||
                <div class="form-group">
 | 
						||
                    <label class="form-label">Sort by</label>
 | 
						||
                    <select id="sortBy" class="form-select" onchange="sortTools()">
 | 
						||
                        <option value="name">Name</option>
 | 
						||
                        <option value="type">Type</option>
 | 
						||
                        <option value="skillLevel">Skill Level</option>
 | 
						||
                        <option value="domains">Domain</option>
 | 
						||
                    </select>
 | 
						||
                </div>
 | 
						||
            </div>
 | 
						||
            <div class="bulk-actions">
 | 
						||
                <button class="btn btn-warning btn-sm" onclick="validateAllTools()">Validate All</button>
 | 
						||
                <button class="btn btn-danger btn-sm" onclick="fixMissingUrls()">Fix Missing URLs</button>
 | 
						||
                <button class="btn btn-secondary btn-sm" onclick="clearAllFilters()">Clear Filters</button>
 | 
						||
                <button class="btn btn-secondary btn-sm" onclick="toggleBulkPanel()">Bulk Actions</button>
 | 
						||
                <button class="btn btn-secondary btn-sm" onclick="showStats()">Statistics</button>
 | 
						||
            </div>
 | 
						||
        </section>
 | 
						||
 | 
						||
        <div class="bulk-panel" id="bulkPanel">
 | 
						||
            <h3>Bulk Operations</h3>
 | 
						||
            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 10px;">
 | 
						||
                <div>
 | 
						||
                    <button class="btn btn-warning btn-sm" onclick="bulkRemoveTags()">Remove Tags</button>
 | 
						||
                    <input type="text" id="bulkRemoveTagsInput" placeholder="Tags to remove (comma-separated)" class="form-input" style="margin-top: 5px; font-size: 12px;">
 | 
						||
                </div>
 | 
						||
                <div>
 | 
						||
                    <button class="btn btn-warning btn-sm" onclick="bulkRenameTags()">Rename Tags</button>
 | 
						||
                    <input type="text" id="bulkRenameFrom" placeholder="From tag" class="form-input" style="margin-top: 5px; font-size: 12px;">
 | 
						||
                    <input type="text" id="bulkRenameTo" placeholder="To tag" class="form-input" style="margin-top: 5px; font-size: 12px;">
 | 
						||
                </div>
 | 
						||
                <div>
 | 
						||
                    <button class="btn btn-success btn-sm" onclick="bulkAssignRelatedConcepts()">Assign Related Concepts</button>
 | 
						||
                    <input type="text" id="bulkConceptsInput" placeholder="Concepts (comma-separated)" class="form-input" style="margin-top: 5px; font-size: 12px;">
 | 
						||
                </div>
 | 
						||
                <div>
 | 
						||
                    <button class="btn btn-success btn-sm" onclick="bulkAssignRelatedSoftware()">Assign Related Software</button>
 | 
						||
                    <input type="text" id="bulkSoftwareInput" placeholder="Software (comma-separated)" class="form-input" style="margin-top: 5px; font-size: 12px;">
 | 
						||
                </div>
 | 
						||
            </div>
 | 
						||
        </div>
 | 
						||
 | 
						||
        <div class="main-content">
 | 
						||
            <section class="tools-section">
 | 
						||
                <div class="tools-header">
 | 
						||
                    <h2>Tools (<span id="toolCount">0</span>)</h2>
 | 
						||
                    <div style="display: flex; gap: 10px; align-items: center;">
 | 
						||
                        <div id="validationSummary" style="display: none; padding: 5px 10px; background: var(--warning-color); color: white; border-radius: 4px; font-size: 0.9em;">
 | 
						||
                            <span id="validationCount">0</span> tools need fixing
 | 
						||
                        </div>
 | 
						||
                        <button class="btn btn-danger btn-sm" onclick="clearSelection()">Clear Selection</button>
 | 
						||
                    </div>
 | 
						||
                </div>
 | 
						||
                <div class="stats" id="statsContainer" style="display: none;"></div>
 | 
						||
                <div class="validation-errors" id="validationErrors"></div>
 | 
						||
                <div class="tools-list" id="toolsList"></div>
 | 
						||
            </section>
 | 
						||
 | 
						||
            <section class="edit-section">
 | 
						||
                <h2 id="editTitle">Tool Editor</h2>
 | 
						||
                <form id="toolForm">
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Name *</label>
 | 
						||
                        <input type="text" id="name" class="form-input" required>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Type *</label>
 | 
						||
                        <select id="type" class="form-select" required>
 | 
						||
                            <option value="">Select type...</option>
 | 
						||
                            <option value="software">Software</option>
 | 
						||
                            <option value="method">Method</option>
 | 
						||
                            <option value="concept">Concept</option>
 | 
						||
                        </select>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Icon</label>
 | 
						||
                        <input type="text" id="icon" class="form-input" placeholder="🔧">
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Description *</label>
 | 
						||
                        <textarea id="description" class="form-textarea" required placeholder="Detailed description of the tool..."></textarea>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">URL *</label>
 | 
						||
                        <input type="url" id="url" class="form-input" required placeholder="https://example.com">
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Project URL</label>
 | 
						||
                        <input type="url" id="projectUrl" class="form-input" placeholder="https://project.example.com">
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Skill Level *</label>
 | 
						||
                        <select id="skillLevel" class="form-select" required>
 | 
						||
                            <option value="">Select level...</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>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">License</label>
 | 
						||
                        <input type="text" id="license" class="form-input" placeholder="MIT, GPL-3.0, Proprietary...">
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Access Type</label>
 | 
						||
                        <select id="accessType" class="form-select">
 | 
						||
                            <option value="">Select access type...</option>
 | 
						||
                            <option value="download">Download</option>
 | 
						||
                            <option value="web">Web</option>
 | 
						||
                            <option value="api">API</option>
 | 
						||
                            <option value="cli">CLI</option>
 | 
						||
                            <option value="commercial">Commercial</option>
 | 
						||
                            <option value="server-based">Server-based</option>
 | 
						||
                        </select>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Domains</label>
 | 
						||
                        <div class="tag-input-container">
 | 
						||
                            <div class="tag-input" id="domainsInput" onclick="focusTagInput('domainsInput')">
 | 
						||
                                <input type="text" placeholder="Type domain..." onkeydown="handleTagInput(event, 'domains')" oninput="showAutocomplete(event, 'domains')">
 | 
						||
                            </div>
 | 
						||
                            <div class="autocomplete-dropdown" id="domainsAutocomplete"></div>
 | 
						||
                        </div>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Phases</label>
 | 
						||
                        <div class="tag-input-container">
 | 
						||
                            <div class="tag-input" id="phasesInput" onclick="focusTagInput('phasesInput')">
 | 
						||
                                <input type="text" placeholder="Type phase..." onkeydown="handleTagInput(event, 'phases')" oninput="showAutocomplete(event, 'phases')">
 | 
						||
                            </div>
 | 
						||
                            <div class="autocomplete-dropdown" id="phasesAutocomplete"></div>
 | 
						||
                        </div>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Platforms</label>
 | 
						||
                        <div class="tag-input-container">
 | 
						||
                            <div class="tag-input" id="platformsInput" onclick="focusTagInput('platformsInput')">
 | 
						||
                                <input type="text" placeholder="Type platform..." onkeydown="handleTagInput(event, 'platforms')" oninput="showAutocomplete(event, 'platforms')">
 | 
						||
                            </div>
 | 
						||
                            <div class="autocomplete-dropdown" id="platformsAutocomplete"></div>
 | 
						||
                        </div>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Tags</label>
 | 
						||
                        <div class="tag-input-container">
 | 
						||
                            <div class="tag-input" id="tagsInput" onclick="focusTagInput('tagsInput')">
 | 
						||
                                <input type="text" placeholder="Type tag..." onkeydown="handleTagInput(event, 'tags')" oninput="showAutocomplete(event, 'tags')">
 | 
						||
                            </div>
 | 
						||
                            <div class="autocomplete-dropdown" id="tagsAutocomplete"></div>
 | 
						||
                        </div>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Related Concepts</label>
 | 
						||
                        <div class="tag-input-container">
 | 
						||
                            <div class="tag-input" id="related_conceptsInput" onclick="focusTagInput('related_conceptsInput')">
 | 
						||
                                <input type="text" placeholder="Type concept..." onkeydown="handleTagInput(event, 'related_concepts')" oninput="showAutocomplete(event, 'related_concepts')">
 | 
						||
                            </div>
 | 
						||
                            <div class="autocomplete-dropdown" id="related_conceptsAutocomplete"></div>
 | 
						||
                        </div>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label class="form-label">Related Software</label>
 | 
						||
                        <div class="tag-input-container">
 | 
						||
                            <div class="tag-input" id="related_softwareInput" onclick="focusTagInput('related_softwareInput')">
 | 
						||
                                <input type="text" placeholder="Type software..." onkeydown="handleTagInput(event, 'related_software')" oninput="showAutocomplete(event, 'related_software')">
 | 
						||
                            </div>
 | 
						||
                            <div class="autocomplete-dropdown" id="related_softwareAutocomplete"></div>
 | 
						||
                        </div>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group">
 | 
						||
                        <label>
 | 
						||
                            <input type="checkbox" id="knowledgebase" class="form-checkbox">
 | 
						||
                            Has Knowledgebase
 | 
						||
                        </label>
 | 
						||
                    </div>
 | 
						||
 | 
						||
                    <div class="form-group" style="display: flex; gap: 10px; margin-top: 20px;">
 | 
						||
                        <button type="button" class="btn btn-primary" onclick="saveTool()">Save Tool</button>
 | 
						||
                        <button type="button" class="btn btn-secondary" onclick="clearForm()">Clear</button>
 | 
						||
                        <button type="button" class="btn btn-danger" onclick="deleteTool()" id="deleteBtn" style="display: none;">Delete</button>
 | 
						||
                    </div>
 | 
						||
                </form>
 | 
						||
            </section>
 | 
						||
        </div>
 | 
						||
    </div>
 | 
						||
 | 
						||
    <div class="status-bar">
 | 
						||
        <div id="statusText">Ready</div>
 | 
						||
        <div style="font-size: 0.8em; color: var(--text-secondary);">
 | 
						||
            Tools.yaml Editor v1.0 | 
 | 
						||
            Shortcuts: Ctrl+S (Save), Ctrl+N (New), Ctrl+E (Export), Alt+V (Validate), Ctrl+F (Search), Esc (Clear)
 | 
						||
        </div>
 | 
						||
    </div>
 | 
						||
 | 
						||
    <script>
 | 
						||
        // Global data storage
 | 
						||
        let toolsData = {
 | 
						||
            tools: [],
 | 
						||
            domains: [],
 | 
						||
            phases: [],
 | 
						||
            'domain-agnostic-software': [],
 | 
						||
            scenarios: [],
 | 
						||
            skill_levels: {}
 | 
						||
        };
 | 
						||
        
 | 
						||
        let filteredTools = [];
 | 
						||
        let selectedTool = null;
 | 
						||
        let editingIndex = -1;
 | 
						||
 | 
						||
        // Initialize the application
 | 
						||
        function init() {
 | 
						||
            // Load theme preference
 | 
						||
            const savedTheme = localStorage.getItem('tools-editor-theme') || 'dark';
 | 
						||
            document.body.setAttribute('data-theme', savedTheme);
 | 
						||
            const themeToggle = document.querySelector('.theme-toggle');
 | 
						||
            themeToggle.textContent = savedTheme === 'light' ? '🌙' : '☀️';
 | 
						||
            
 | 
						||
            // Load sample domains and phases if none exist
 | 
						||
            if (toolsData.domains.length === 0) {
 | 
						||
                toolsData.domains = [
 | 
						||
                    { id: 'incident-response', name: 'Incident Response' },
 | 
						||
                    { id: 'static-investigations', name: 'Static Investigations' },
 | 
						||
                    { id: 'malware-analysis', name: 'Malware Analysis' },
 | 
						||
                    { id: 'network-forensics', name: 'Network Forensics' },
 | 
						||
                    { id: 'mobile-forensics', name: 'Mobile Forensics' },
 | 
						||
                    { id: 'cloud-forensics', name: 'Cloud Forensics' }
 | 
						||
                ];
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (toolsData.phases.length === 0) {
 | 
						||
                toolsData.phases = [
 | 
						||
                    { id: 'data-collection', name: 'Data Collection' },
 | 
						||
                    { id: 'examination', name: 'Examination' },
 | 
						||
                    { id: 'analysis', name: 'Analysis' },
 | 
						||
                    { id: 'reporting', name: 'Reporting' }
 | 
						||
                ];
 | 
						||
            }
 | 
						||
 | 
						||
            populateFilters();
 | 
						||
            renderTools();
 | 
						||
            updateStatus('Application initialized');
 | 
						||
        }
 | 
						||
 | 
						||
        // Theme management
 | 
						||
        function toggleTheme() {
 | 
						||
            const body = document.body;
 | 
						||
            const currentTheme = body.getAttribute('data-theme');
 | 
						||
            const newTheme = currentTheme === 'light' ? 'dark' : 'light';
 | 
						||
            body.setAttribute('data-theme', newTheme);
 | 
						||
            
 | 
						||
            // Save theme preference
 | 
						||
            localStorage.setItem('tools-editor-theme', newTheme);
 | 
						||
            
 | 
						||
            const themeToggle = document.querySelector('.theme-toggle');
 | 
						||
            themeToggle.textContent = newTheme === 'light' ? '🌙' : '☀️';
 | 
						||
            
 | 
						||
            updateStatus(`Switched to ${newTheme} theme`);
 | 
						||
        }
 | 
						||
 | 
						||
        // Status updates
 | 
						||
        function updateStatus(message) {
 | 
						||
            document.getElementById('statusText').textContent = message;
 | 
						||
        }
 | 
						||
 | 
						||
        // Populate filter dropdowns
 | 
						||
        function populateFilters() {
 | 
						||
            const domainFilter = document.getElementById('domainFilter');
 | 
						||
            domainFilter.innerHTML = '<option value="">All Domains</option>';
 | 
						||
            toolsData.domains.forEach(domain => {
 | 
						||
                const option = document.createElement('option');
 | 
						||
                option.value = domain.id;
 | 
						||
                option.textContent = domain.name;
 | 
						||
                domainFilter.appendChild(option);
 | 
						||
            });
 | 
						||
        }
 | 
						||
 | 
						||
        // Get all unique values for autocomplete
 | 
						||
        function getUniqueValues(field) {
 | 
						||
            const values = new Set();
 | 
						||
            toolsData.tools.forEach(tool => {
 | 
						||
                if (tool[field]) {
 | 
						||
                    if (Array.isArray(tool[field])) {
 | 
						||
                        tool[field].forEach(value => values.add(value));
 | 
						||
                    } else {
 | 
						||
                        values.add(tool[field]);
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            // Add predefined values
 | 
						||
            if (field === 'domains') {
 | 
						||
                toolsData.domains.forEach(domain => values.add(domain.id));
 | 
						||
            } else if (field === 'phases') {
 | 
						||
                toolsData.phases.forEach(phase => values.add(phase.id));
 | 
						||
            } else if (field === 'platforms') {
 | 
						||
                ['Windows', 'macOS', 'Linux', 'Web', 'Mobile', 'Cross-platform'].forEach(p => values.add(p));
 | 
						||
            } else if (field === 'related_concepts') {
 | 
						||
                toolsData.tools.filter(t => t.type === 'concept').forEach(t => values.add(t.name));
 | 
						||
            } else if (field === 'related_software') {
 | 
						||
                toolsData.tools.filter(t => t.type === 'software').forEach(t => values.add(t.name));
 | 
						||
            }
 | 
						||
            
 | 
						||
            return Array.from(values).sort();
 | 
						||
        }
 | 
						||
 | 
						||
        // Tag input handling
 | 
						||
        function focusTagInput(containerId) {
 | 
						||
            const input = document.querySelector(`#${containerId} input`);
 | 
						||
            input.focus();
 | 
						||
        }
 | 
						||
 | 
						||
        function handleTagInput(event, field) {
 | 
						||
            const dropdown = document.getElementById(`${field}Autocomplete`);
 | 
						||
            const items = dropdown.querySelectorAll('.autocomplete-item');
 | 
						||
            let selectedIndex = Array.from(items).findIndex(item => item.classList.contains('selected'));
 | 
						||
            
 | 
						||
            if (event.key === 'ArrowDown') {
 | 
						||
                event.preventDefault();
 | 
						||
                selectedIndex = Math.min(selectedIndex + 1, items.length - 1);
 | 
						||
                updateAutocompleteSelection(items, selectedIndex);
 | 
						||
            } else if (event.key === 'ArrowUp') {
 | 
						||
                event.preventDefault();
 | 
						||
                selectedIndex = Math.max(selectedIndex - 1, 0);
 | 
						||
                updateAutocompleteSelection(items, selectedIndex);
 | 
						||
            } else if (event.key === 'Enter') {
 | 
						||
                event.preventDefault();
 | 
						||
                if (selectedIndex >= 0 && items[selectedIndex]) {
 | 
						||
                    const value = items[selectedIndex].textContent;
 | 
						||
                    addTag(field, value);
 | 
						||
                    event.target.value = '';
 | 
						||
                    hideAutocomplete(field);
 | 
						||
                } else {
 | 
						||
                    const value = event.target.value.trim();
 | 
						||
                    if (value) {
 | 
						||
                        addTag(field, value);
 | 
						||
                        event.target.value = '';
 | 
						||
                        hideAutocomplete(field);
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            } else if (event.key === ',' || event.key === 'Tab') {
 | 
						||
                event.preventDefault();
 | 
						||
                const input = event.target;
 | 
						||
                const value = input.value.trim();
 | 
						||
                if (value) {
 | 
						||
                    addTag(field, value);
 | 
						||
                    input.value = '';
 | 
						||
                    hideAutocomplete(field);
 | 
						||
                }
 | 
						||
            } else if (event.key === 'Backspace' && event.target.value === '') {
 | 
						||
                removeLastTag(field);
 | 
						||
            }
 | 
						||
        }
 | 
						||
        
 | 
						||
        function updateAutocompleteSelection(items, selectedIndex) {
 | 
						||
            items.forEach((item, index) => {
 | 
						||
                item.classList.toggle('selected', index === selectedIndex);
 | 
						||
            });
 | 
						||
        }
 | 
						||
 | 
						||
        function addTag(field, value) {
 | 
						||
            const container = document.getElementById(`${field}Input`);
 | 
						||
            const input = container.querySelector('input');
 | 
						||
            
 | 
						||
            // Check if tag already exists
 | 
						||
            const existingTags = Array.from(container.querySelectorAll('.tag-input-item')).map(item => 
 | 
						||
                item.textContent.replace('×', '').trim()
 | 
						||
            );
 | 
						||
            
 | 
						||
            if (existingTags.includes(value)) return;
 | 
						||
            
 | 
						||
            const tagElement = document.createElement('div');
 | 
						||
            tagElement.className = 'tag-input-item';
 | 
						||
            tagElement.innerHTML = `${value} <span class="remove" onclick="removeTag('${field}', this)">×</span>`;
 | 
						||
            
 | 
						||
            container.insertBefore(tagElement, input);
 | 
						||
        }
 | 
						||
 | 
						||
        function removeTag(field, element) {
 | 
						||
            element.parentElement.remove();
 | 
						||
        }
 | 
						||
 | 
						||
        function removeLastTag(field) {
 | 
						||
            const container = document.getElementById(`${field}Input`);
 | 
						||
            const tags = container.querySelectorAll('.tag-input-item');
 | 
						||
            if (tags.length > 0) {
 | 
						||
                tags[tags.length - 1].remove();
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        function getTagValues(field) {
 | 
						||
            const container = document.getElementById(`${field}Input`);
 | 
						||
            return Array.from(container.querySelectorAll('.tag-input-item')).map(item => 
 | 
						||
                item.textContent.replace('×', '').trim()
 | 
						||
            );
 | 
						||
        }
 | 
						||
 | 
						||
        function setTagValues(field, values) {
 | 
						||
            const container = document.getElementById(`${field}Input`);
 | 
						||
            // Clear existing tags
 | 
						||
            container.querySelectorAll('.tag-input-item').forEach(item => item.remove());
 | 
						||
            // Add new tags
 | 
						||
            if (values && Array.isArray(values)) {
 | 
						||
                values.forEach(value => addTag(field, value));
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        // Autocomplete functionality
 | 
						||
        function showAutocomplete(event, field) {
 | 
						||
            const input = event.target;
 | 
						||
            const value = input.value.toLowerCase();
 | 
						||
            const dropdown = document.getElementById(`${field}Autocomplete`);
 | 
						||
            
 | 
						||
            if (value.length < 1) {
 | 
						||
                hideAutocomplete(field);
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            const suggestions = getUniqueValues(field).filter(item => 
 | 
						||
                item.toLowerCase().includes(value)
 | 
						||
            );
 | 
						||
            
 | 
						||
            if (suggestions.length === 0) {
 | 
						||
                hideAutocomplete(field);
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            dropdown.innerHTML = '';
 | 
						||
            suggestions.slice(0, 10).forEach((suggestion, index) => {
 | 
						||
                const item = document.createElement('div');
 | 
						||
                item.className = 'autocomplete-item';
 | 
						||
                if (index === 0) item.classList.add('selected'); // Select first item by default
 | 
						||
                item.textContent = suggestion;
 | 
						||
                item.onclick = () => {
 | 
						||
                    addTag(field, suggestion);
 | 
						||
                    input.value = '';
 | 
						||
                    hideAutocomplete(field);
 | 
						||
                };
 | 
						||
                dropdown.appendChild(item);
 | 
						||
            });
 | 
						||
            
 | 
						||
            dropdown.style.display = 'block';
 | 
						||
        }
 | 
						||
 | 
						||
        function hideAutocomplete(field) {
 | 
						||
            document.getElementById(`${field}Autocomplete`).style.display = 'none';
 | 
						||
        }
 | 
						||
 | 
						||
        // Tool management
 | 
						||
        function addNewTool() {
 | 
						||
            clearForm();
 | 
						||
            editingIndex = -1;
 | 
						||
            document.getElementById('editTitle').textContent = 'Add New Tool';
 | 
						||
            document.getElementById('deleteBtn').style.display = 'none';
 | 
						||
            updateStatus('Creating new tool');
 | 
						||
        }
 | 
						||
 | 
						||
        function editTool(index) {
 | 
						||
            const tool = filteredTools[index];
 | 
						||
            editingIndex = toolsData.tools.indexOf(tool);
 | 
						||
            
 | 
						||
            // Populate form
 | 
						||
            document.getElementById('name').value = tool.name || '';
 | 
						||
            document.getElementById('type').value = tool.type || '';
 | 
						||
            document.getElementById('icon').value = tool.icon || '';
 | 
						||
            document.getElementById('description').value = tool.description || '';
 | 
						||
            document.getElementById('url').value = tool.url || '';
 | 
						||
            document.getElementById('projectUrl').value = tool.projectUrl || '';
 | 
						||
            document.getElementById('skillLevel').value = tool.skillLevel || '';
 | 
						||
            document.getElementById('license').value = tool.license || '';
 | 
						||
            document.getElementById('accessType').value = tool.accessType || '';
 | 
						||
            document.getElementById('knowledgebase').checked = tool.knowledgebase || false;
 | 
						||
            
 | 
						||
            // Set tag values
 | 
						||
            setTagValues('domains', tool.domains);
 | 
						||
            setTagValues('phases', tool.phases);
 | 
						||
            setTagValues('platforms', tool.platforms);
 | 
						||
            setTagValues('tags', tool.tags);
 | 
						||
            setTagValues('related_concepts', tool.related_concepts);
 | 
						||
            setTagValues('related_software', tool.related_software);
 | 
						||
            
 | 
						||
            document.getElementById('editTitle').textContent = `Edit: ${tool.name}`;
 | 
						||
            document.getElementById('deleteBtn').style.display = 'inline-block';
 | 
						||
            
 | 
						||
            updateStatus(`Editing ${tool.name}`);
 | 
						||
        }
 | 
						||
 | 
						||
        function saveTool() {
 | 
						||
            const form = document.getElementById('toolForm');
 | 
						||
            if (!form.checkValidity()) {
 | 
						||
                form.reportValidity();
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            const tool = {
 | 
						||
                name: document.getElementById('name').value.trim(),
 | 
						||
                type: document.getElementById('type').value,
 | 
						||
                description: document.getElementById('description').value.trim(),
 | 
						||
                url: document.getElementById('url').value.trim(),
 | 
						||
                skillLevel: document.getElementById('skillLevel').value,
 | 
						||
                domains: getTagValues('domains'),
 | 
						||
                phases: getTagValues('phases'),
 | 
						||
                platforms: getTagValues('platforms'),
 | 
						||
                tags: getTagValues('tags'),
 | 
						||
                related_concepts: getTagValues('related_concepts'),
 | 
						||
                related_software: getTagValues('related_software')
 | 
						||
            };
 | 
						||
            
 | 
						||
            // Add optional fields only if they have values
 | 
						||
            const icon = document.getElementById('icon').value.trim();
 | 
						||
            if (icon) tool.icon = icon;
 | 
						||
            
 | 
						||
            const projectUrl = document.getElementById('projectUrl').value.trim();
 | 
						||
            if (projectUrl) tool.projectUrl = projectUrl;
 | 
						||
            
 | 
						||
            const license = document.getElementById('license').value.trim();
 | 
						||
            if (license) tool.license = license;
 | 
						||
            
 | 
						||
            const accessType = document.getElementById('accessType').value;
 | 
						||
            if (accessType) tool.accessType = accessType;
 | 
						||
            
 | 
						||
            if (document.getElementById('knowledgebase').checked) {
 | 
						||
                tool.knowledgebase = true;
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Remove empty arrays to keep YAML clean
 | 
						||
            Object.keys(tool).forEach(key => {
 | 
						||
                if (Array.isArray(tool[key]) && tool[key].length === 0) {
 | 
						||
                    delete tool[key];
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            // Validate tool
 | 
						||
            const errors = validateTool(tool);
 | 
						||
            if (errors.length > 0) {
 | 
						||
                showValidationErrors(errors);
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            hideValidationErrors();
 | 
						||
            
 | 
						||
            const isNewTool = editingIndex < 0;
 | 
						||
            const toolName = tool.name;
 | 
						||
            
 | 
						||
            if (editingIndex >= 0) {
 | 
						||
                // Update existing tool
 | 
						||
                toolsData.tools[editingIndex] = tool;
 | 
						||
                
 | 
						||
                // Show immediate feedback
 | 
						||
                updateStatus(`✅ Updated ${toolName}`);
 | 
						||
                
 | 
						||
                // Add temporary success indicator to the form
 | 
						||
                showSaveSuccess('Tool updated successfully!');
 | 
						||
                
 | 
						||
            } else {
 | 
						||
                // Add new tool
 | 
						||
                toolsData.tools.push(tool);
 | 
						||
                
 | 
						||
                // Show immediate feedback
 | 
						||
                updateStatus(`✅ Added ${toolName}`);
 | 
						||
                
 | 
						||
                // Add temporary success indicator to the form
 | 
						||
                showSaveSuccess('Tool added successfully!');
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Refresh the view immediately - this will update filtered results
 | 
						||
            filterTools(); // This ensures tools disappear from "invalid" filter if fixed
 | 
						||
            
 | 
						||
            // Clear form after successful save
 | 
						||
            clearForm();
 | 
						||
            
 | 
						||
            // If we were viewing validation errors, update the validation summary
 | 
						||
            setTimeout(() => {
 | 
						||
                const validationFilter = document.getElementById('validationFilter').value;
 | 
						||
                if (validationFilter === 'invalid') {
 | 
						||
                    // Re-run validation check to update counts
 | 
						||
                    const remainingErrors = [];
 | 
						||
                    toolsData.tools.forEach((t, index) => {
 | 
						||
                        const toolErrors = validateTool(t, index);
 | 
						||
                        remainingErrors.push(...toolErrors);
 | 
						||
                    });
 | 
						||
                    
 | 
						||
                    if (remainingErrors.length === 0) {
 | 
						||
                        // No more invalid tools - show success message
 | 
						||
                        hideValidationErrors();
 | 
						||
                        updateStatus('🎉 All validation errors fixed!');
 | 
						||
                        
 | 
						||
                        // Optionally clear the validation filter
 | 
						||
                        document.getElementById('validationFilter').value = '';
 | 
						||
                        filterTools();
 | 
						||
                    } else {
 | 
						||
                        // Update validation summary
 | 
						||
                        const validationSummary = document.getElementById('validationSummary');
 | 
						||
                        const validationCount = document.getElementById('validationCount');
 | 
						||
                        if (validationSummary && validationCount) {
 | 
						||
                            validationCount.textContent = toolsData.tools.filter(t => validateToolBasic(t).length > 0).length;
 | 
						||
                            validationSummary.style.display = validationCount.textContent === '0' ? 'none' : 'block';
 | 
						||
                        }
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            }, 100);
 | 
						||
        }
 | 
						||
 | 
						||
        // Helper function to show save success feedback
 | 
						||
        function showSaveSuccess(message) {
 | 
						||
            // Create or update success indicator
 | 
						||
            let successDiv = document.getElementById('saveSuccess');
 | 
						||
            if (!successDiv) {
 | 
						||
                successDiv = document.createElement('div');
 | 
						||
                successDiv.id = 'saveSuccess';
 | 
						||
                successDiv.style.cssText = `
 | 
						||
                    position: fixed;
 | 
						||
                    top: 20px;
 | 
						||
                    right: 20px;
 | 
						||
                    background: var(--success-color);
 | 
						||
                    color: white;
 | 
						||
                    padding: 12px 20px;
 | 
						||
                    border-radius: 6px;
 | 
						||
                    box-shadow: var(--shadow);
 | 
						||
                    z-index: 1000;
 | 
						||
                    opacity: 0;
 | 
						||
                    transform: translateY(-20px);
 | 
						||
                    transition: all 0.3s ease;
 | 
						||
                `;
 | 
						||
                document.body.appendChild(successDiv);
 | 
						||
            }
 | 
						||
            
 | 
						||
            successDiv.textContent = message;
 | 
						||
            
 | 
						||
            // Animate in
 | 
						||
            requestAnimationFrame(() => {
 | 
						||
                successDiv.style.opacity = '1';
 | 
						||
                successDiv.style.transform = 'translateY(0)';
 | 
						||
            });
 | 
						||
            
 | 
						||
            // Animate out after 3 seconds
 | 
						||
            setTimeout(() => {
 | 
						||
                successDiv.style.opacity = '0';
 | 
						||
                successDiv.style.transform = 'translateY(-20px)';
 | 
						||
                
 | 
						||
                setTimeout(() => {
 | 
						||
                    if (successDiv.parentNode) {
 | 
						||
                        successDiv.parentNode.removeChild(successDiv);
 | 
						||
                    }
 | 
						||
                }, 300);
 | 
						||
            }, 3000);
 | 
						||
        }
 | 
						||
 | 
						||
        function deleteTool() {
 | 
						||
            if (editingIndex >= 0 && confirm('Are you sure you want to delete this tool?')) {
 | 
						||
                const toolName = toolsData.tools[editingIndex].name;
 | 
						||
                toolsData.tools.splice(editingIndex, 1);
 | 
						||
                updateStatus(`Deleted ${toolName}`);
 | 
						||
                renderTools();
 | 
						||
                clearForm();
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        function clearForm() {
 | 
						||
            document.getElementById('toolForm').reset();
 | 
						||
            // Clear tag inputs
 | 
						||
            ['domains', 'phases', 'platforms', 'tags', 'related_concepts', 'related_software'].forEach(field => {
 | 
						||
                setTagValues(field, []);
 | 
						||
            });
 | 
						||
            editingIndex = -1;
 | 
						||
            document.getElementById('editTitle').textContent = 'Tool Editor';
 | 
						||
            document.getElementById('deleteBtn').style.display = 'none';
 | 
						||
        }
 | 
						||
 | 
						||
        // Basic validation for rendering (no duplicate checks)
 | 
						||
        function validateToolBasic(tool) {
 | 
						||
            const errors = [];
 | 
						||
            
 | 
						||
            // Required fields validation
 | 
						||
            if (!tool.name || typeof tool.name !== 'string' || tool.name.trim() === '') {
 | 
						||
                errors.push('Name is required and must be a non-empty string');
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!tool.type || !['software', 'method', 'concept'].includes(tool.type)) {
 | 
						||
                errors.push('Type is required and must be one of: software, method, concept');
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!tool.description || typeof tool.description !== 'string' || tool.description.trim() === '') {
 | 
						||
                errors.push('Description is required and must be a non-empty string');
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!tool.url || typeof tool.url !== 'string' || tool.url.trim() === '') {
 | 
						||
                errors.push('URL is required and must be a non-empty string');
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!tool.skillLevel || !['novice', 'beginner', 'intermediate', 'advanced', 'expert'].includes(tool.skillLevel)) {
 | 
						||
                errors.push('Skill level is required and must be one of: novice, beginner, intermediate, advanced, expert');
 | 
						||
            }
 | 
						||
            
 | 
						||
            // URL format validation
 | 
						||
            if (tool.url && tool.url.trim() !== '') {
 | 
						||
                try {
 | 
						||
                    new URL(tool.url);
 | 
						||
                } catch {
 | 
						||
                    errors.push('URL must be a valid URL format');
 | 
						||
                }
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Project URL validation (if provided)
 | 
						||
            if (tool.projectUrl && tool.projectUrl.trim() !== '') {
 | 
						||
                try {
 | 
						||
                    new URL(tool.projectUrl);
 | 
						||
                } catch {
 | 
						||
                    errors.push('Project URL must be a valid URL format');
 | 
						||
                }
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Array fields validation
 | 
						||
            const arrayFields = ['domains', 'phases', 'platforms', 'tags', 'related_concepts', 'related_software'];
 | 
						||
            arrayFields.forEach(field => {
 | 
						||
                if (tool[field] !== undefined && tool[field] !== null && !Array.isArray(tool[field])) {
 | 
						||
                    errors.push(`${field} must be an array if provided`);
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            // String fields validation (optional but must be strings if provided)
 | 
						||
            const stringFields = ['icon', 'license', 'accessType'];
 | 
						||
            stringFields.forEach(field => {
 | 
						||
                if (tool[field] !== undefined && tool[field] !== null && typeof tool[field] !== 'string') {
 | 
						||
                    errors.push(`${field} must be a string if provided`);
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            // Boolean field validation
 | 
						||
            if (tool.knowledgebase !== undefined && tool.knowledgebase !== null && typeof tool.knowledgebase !== 'boolean') {
 | 
						||
                errors.push('knowledgebase must be a boolean if provided');
 | 
						||
            }
 | 
						||
            
 | 
						||
            return errors;
 | 
						||
        }
 | 
						||
 | 
						||
        // Full validation for form submission and bulk validation (includes duplicate checks)
 | 
						||
        function validateTool(tool, index = null) {
 | 
						||
            const prefix = index !== null ? `Tool ${index + 1} (${tool.name || 'Unnamed'}): ` : '';
 | 
						||
            const errors = validateToolBasic(tool).map(error => prefix + error);
 | 
						||
            
 | 
						||
            // Check for duplicate names (only for single tool validation)
 | 
						||
            if (index === null) {
 | 
						||
                const existingIndex = toolsData.tools.findIndex(t => t.name === tool.name);
 | 
						||
                if (existingIndex >= 0 && existingIndex !== editingIndex) {
 | 
						||
                    errors.push(prefix + 'Tool name already exists');
 | 
						||
                }
 | 
						||
            }
 | 
						||
            
 | 
						||
            return errors;
 | 
						||
        }
 | 
						||
 | 
						||
        // Update the renderTools function to use basic validation
 | 
						||
        // Find this line in the renderTools function:
 | 
						||
        // And replace it with:
 | 
						||
        // const hasValidationIssues = validateToolBasic(tool).length > 0;
 | 
						||
 | 
						||
        function fixMissingUrls() {
 | 
						||
            const toolsWithMissingUrls = toolsData.tools.filter(tool => 
 | 
						||
                !tool.url || typeof tool.url !== 'string' || tool.url.trim() === ''
 | 
						||
            );
 | 
						||
            
 | 
						||
            if (toolsWithMissingUrls.length === 0) {
 | 
						||
                updateStatus('✅ No tools with missing URLs found');
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            const confirmMessage = `Found ${toolsWithMissingUrls.length} tools with missing URLs:\n\n${toolsWithMissingUrls.map(t => `• ${t.name || 'Unnamed'}`).join('\n')}\n\nFix by adding placeholder URLs?`;
 | 
						||
            
 | 
						||
            if (confirm(confirmMessage)) {
 | 
						||
                let fixedCount = 0;
 | 
						||
                toolsData.tools.forEach(tool => {
 | 
						||
                    if (!tool.url || typeof tool.url !== 'string' || tool.url.trim() === '') {
 | 
						||
                        tool.url = 'https://example.com'; // Placeholder URL
 | 
						||
                        fixedCount++;
 | 
						||
                    }
 | 
						||
                });
 | 
						||
                
 | 
						||
                updateStatus(`🔧 Fixed ${fixedCount} tools with missing URLs (added placeholder URLs)`);
 | 
						||
                renderTools();
 | 
						||
                
 | 
						||
                // Show validation results
 | 
						||
                setTimeout(() => validateAllTools(), 500);
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        function validateAllTools() {
 | 
						||
            const allErrors = [];
 | 
						||
            
 | 
						||
            toolsData.tools.forEach((tool, index) => {
 | 
						||
                const toolErrors = validateTool(tool, index);
 | 
						||
                allErrors.push(...toolErrors);
 | 
						||
            });
 | 
						||
            
 | 
						||
            // Check for duplicate names across all tools
 | 
						||
            const nameMap = new Map();
 | 
						||
            toolsData.tools.forEach((tool, index) => {
 | 
						||
                if (tool.name) {
 | 
						||
                    if (nameMap.has(tool.name)) {
 | 
						||
                        allErrors.push(`Duplicate name "${tool.name}" found at indices ${nameMap.get(tool.name)} and ${index}`);
 | 
						||
                    } else {
 | 
						||
                        nameMap.set(tool.name, index);
 | 
						||
                    }
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            if (allErrors.length === 0) {
 | 
						||
                updateStatus('✅ All tools are valid');
 | 
						||
                hideValidationErrors();
 | 
						||
            } else {
 | 
						||
                updateStatus(`❌ Found ${allErrors.length} validation errors`);
 | 
						||
                showValidationErrors(allErrors);
 | 
						||
                
 | 
						||
                // Scroll to errors
 | 
						||
                document.getElementById('validationErrors').scrollIntoView({ behavior: 'smooth' });
 | 
						||
            }
 | 
						||
            
 | 
						||
            return allErrors.length === 0;
 | 
						||
        }
 | 
						||
 | 
						||
        function showValidationErrors(errors) {
 | 
						||
            const errorDiv = document.getElementById('validationErrors');
 | 
						||
            
 | 
						||
            if (Array.isArray(errors) && errors.length > 10) {
 | 
						||
                // Show first 10 errors and a summary for large error lists
 | 
						||
                const firstTen = errors.slice(0, 10);
 | 
						||
                const remaining = errors.length - 10;
 | 
						||
                errorDiv.innerHTML = `
 | 
						||
                    <strong>Validation Errors (showing first 10 of ${errors.length}):</strong><br>
 | 
						||
                    ${firstTen.map(error => `• ${error}`).join('<br>')}
 | 
						||
                    <br><strong>... and ${remaining} more errors</strong>
 | 
						||
                `;
 | 
						||
            } else {
 | 
						||
                errorDiv.innerHTML = `
 | 
						||
                    <strong>Validation Errors:</strong><br>
 | 
						||
                    ${(Array.isArray(errors) ? errors : [errors]).map(error => `• ${error}`).join('<br>')}
 | 
						||
                `;
 | 
						||
            }
 | 
						||
            
 | 
						||
            errorDiv.style.display = 'block';
 | 
						||
        }
 | 
						||
 | 
						||
        function hideValidationErrors() {
 | 
						||
            document.getElementById('validationErrors').style.display = 'none';
 | 
						||
        }
 | 
						||
 | 
						||
        // Filtering and sorting
 | 
						||
        function filterTools() {
 | 
						||
            const searchTerm = document.getElementById('searchInput').value.toLowerCase();
 | 
						||
            const typeFilter = document.getElementById('typeFilter').value;
 | 
						||
            const skillFilter = document.getElementById('skillFilter').value;
 | 
						||
            const domainFilter = document.getElementById('domainFilter').value;
 | 
						||
            const knowledgebaseFilter = document.getElementById('knowledgebaseFilter').value;
 | 
						||
            const validationFilter = document.getElementById('validationFilter').value;
 | 
						||
            
 | 
						||
            filteredTools = toolsData.tools.filter(tool => {
 | 
						||
                const matchesSearch = !searchTerm || 
 | 
						||
                    tool.name.toLowerCase().includes(searchTerm) ||
 | 
						||
                    tool.description.toLowerCase().includes(searchTerm) ||
 | 
						||
                    (tool.tags && tool.tags.some(tag => tag.toLowerCase().includes(searchTerm)));
 | 
						||
                
 | 
						||
                const matchesType = !typeFilter || tool.type === typeFilter;
 | 
						||
                const matchesSkill = !skillFilter || tool.skillLevel === skillFilter;
 | 
						||
                const matchesDomain = !domainFilter || (tool.domains && tool.domains.includes(domainFilter));
 | 
						||
                const matchesKnowledgebase = !knowledgebaseFilter || 
 | 
						||
                    (knowledgebaseFilter === 'true' && tool.knowledgebase) ||
 | 
						||
                    (knowledgebaseFilter === 'false' && !tool.knowledgebase);
 | 
						||
                
 | 
						||
                // Validation filter
 | 
						||
                let matchesValidation = true;
 | 
						||
                if (validationFilter) {
 | 
						||
                    const hasValidationIssues = validateToolBasic(tool).length > 0;
 | 
						||
                    if (validationFilter === 'valid') {
 | 
						||
                        matchesValidation = !hasValidationIssues;
 | 
						||
                    } else if (validationFilter === 'invalid') {
 | 
						||
                        matchesValidation = hasValidationIssues;
 | 
						||
                    }
 | 
						||
                }
 | 
						||
                
 | 
						||
                return matchesSearch && matchesType && matchesSkill && matchesDomain && matchesKnowledgebase && matchesValidation;
 | 
						||
            });
 | 
						||
            
 | 
						||
            sortTools();
 | 
						||
        }
 | 
						||
 | 
						||
        function showInvalidToolsFilter(errorCount) {
 | 
						||
            // Set validation filter to show only invalid tools
 | 
						||
            document.getElementById('validationFilter').value = 'invalid';
 | 
						||
            filterTools();
 | 
						||
            
 | 
						||
            // Show helpful message
 | 
						||
            showValidationErrors([
 | 
						||
                `⚠️ Import completed with ${errorCount} validation errors.`,
 | 
						||
                '',
 | 
						||
                'The filter has been set to show only tools with validation issues.',
 | 
						||
                'Please fix these errors before exporting to ensure compatibility with the web application.',
 | 
						||
                '',
 | 
						||
                'Click on each tool marked with ⚠️ to edit and resolve the issues.'
 | 
						||
            ]);
 | 
						||
            
 | 
						||
            updateStatus(`⚠️ Import completed - ${errorCount} tools need fixing`);
 | 
						||
        }
 | 
						||
 | 
						||
        function clearAllFilters() {
 | 
						||
            document.getElementById('searchInput').value = '';
 | 
						||
            document.getElementById('typeFilter').value = '';
 | 
						||
            document.getElementById('skillFilter').value = '';
 | 
						||
            document.getElementById('domainFilter').value = '';
 | 
						||
            document.getElementById('knowledgebaseFilter').value = '';
 | 
						||
            document.getElementById('validationFilter').value = '';
 | 
						||
            document.getElementById('sortBy').value = 'name';
 | 
						||
            filterTools();
 | 
						||
            updateStatus('All filters cleared');
 | 
						||
        }
 | 
						||
 | 
						||
        function sortTools() {
 | 
						||
            const sortBy = document.getElementById('sortBy').value;
 | 
						||
            
 | 
						||
            filteredTools.sort((a, b) => {
 | 
						||
                let aVal = a[sortBy] || '';
 | 
						||
                let bVal = b[sortBy] || '';
 | 
						||
                
 | 
						||
                if (Array.isArray(aVal)) aVal = aVal.join(', ');
 | 
						||
                if (Array.isArray(bVal)) bVal = bVal.join(', ');
 | 
						||
                
 | 
						||
                return aVal.toString().localeCompare(bVal.toString());
 | 
						||
            });
 | 
						||
            
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        // Rendering
 | 
						||
        function renderTools() {
 | 
						||
            const container = document.getElementById('toolsList');
 | 
						||
            const countElement = document.getElementById('toolCount');
 | 
						||
            if (!filteredTools.length) {
 | 
						||
                countElement.textContent = 0;
 | 
						||
                container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-secondary);">No tools found</div>';
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            countElement.textContent = filteredTools.length;
 | 
						||
            
 | 
						||
            if (filteredTools.length === 0) {
 | 
						||
                container.innerHTML = '<div style="text-align: center; padding: 40px; color: var(--text-secondary);">No tools found</div>';
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            container.innerHTML = filteredTools.map((tool, index) => {
 | 
						||
                // Quick validation check for visual indicator
 | 
						||
                const hasValidationIssues = validateToolBasic(tool).length > 0;
 | 
						||
                
 | 
						||
                return `
 | 
						||
                <div class="tool-item ${selectedTool === tool ? 'selected' : ''}" onclick="selectTool(${index})">
 | 
						||
                    <div class="tool-header">
 | 
						||
                        <div>
 | 
						||
                            <div class="tool-name">
 | 
						||
                                ${hasValidationIssues ? '⚠️ ' : ''}
 | 
						||
                                ${tool.icon || '🔧'} ${tool.name || 'Unnamed Tool'}
 | 
						||
                                ${tool.knowledgebase ? ' 📖' : ''}
 | 
						||
                                ${tool.projectUrl ? ' 🌐' : ''}
 | 
						||
                            </div>
 | 
						||
                            <div class="tool-meta">
 | 
						||
                                <span>${tool.type || 'Unknown'}</span>
 | 
						||
                                <span>${tool.skillLevel || 'Unknown'}</span>
 | 
						||
                                ${tool.license ? `<span>${tool.license}</span>` : ''}
 | 
						||
                                ${tool.accessType ? `<span>${tool.accessType}</span>` : ''}
 | 
						||
                                ${hasValidationIssues ? '<span style="color: var(--danger-color);">⚠️ Issues</span>' : ''}
 | 
						||
                            </div>
 | 
						||
                        </div>
 | 
						||
                        <button class="btn btn-secondary btn-sm" onclick="event.stopPropagation(); editTool(${index})">Edit</button>
 | 
						||
                    </div>
 | 
						||
                    <div class="tool-description">${tool.description || 'No description provided'}</div>
 | 
						||
                    ${tool.domains && tool.domains.length ? `
 | 
						||
                        <div style="margin-top: 8px; font-size: 0.85em; color: var(--text-secondary);">
 | 
						||
                            <strong>Domains:</strong> ${tool.domains.slice(0, 3).join(', ')}${tool.domains.length > 3 ? '...' : ''}
 | 
						||
                        </div>
 | 
						||
                    ` : ''}
 | 
						||
                    ${tool.tags ? `
 | 
						||
                        <div class="tags">
 | 
						||
                            ${tool.tags.slice(0, 5).map(tag => `<span class="tag">${tag}</span>`).join('')}
 | 
						||
                            ${tool.tags.length > 5 ? `<span class="tag">+${tool.tags.length - 5}</span>` : ''}
 | 
						||
                        </div>
 | 
						||
                    ` : ''}
 | 
						||
                </div>
 | 
						||
            `}).join('');
 | 
						||
        }
 | 
						||
 | 
						||
        function selectTool(index) {
 | 
						||
            selectedTool = filteredTools[index];
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        function clearSelection() {
 | 
						||
            selectedTool = null;
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        // Statistics
 | 
						||
        function showStats() {
 | 
						||
            const stats = calculateStats();
 | 
						||
            const container = document.getElementById('statsContainer');
 | 
						||
            
 | 
						||
            container.innerHTML = Object.entries(stats).map(([key, value]) => `
 | 
						||
                <div class="stat-item">
 | 
						||
                    <div class="stat-number">${value}</div>
 | 
						||
                    <div>${key}</div>
 | 
						||
                </div>
 | 
						||
            `).join('');
 | 
						||
            
 | 
						||
            container.style.display = container.style.display === 'none' ? 'grid' : 'none';
 | 
						||
        }
 | 
						||
 | 
						||
        function calculateStats() {
 | 
						||
            const stats = {
 | 
						||
                'Total Tools': toolsData.tools.length,
 | 
						||
                'Software': toolsData.tools.filter(t => t.type === 'software').length,
 | 
						||
                'Methods': toolsData.tools.filter(t => t.type === 'method').length,
 | 
						||
                'Concepts': toolsData.tools.filter(t => t.type === 'concept').length,
 | 
						||
                'With KB': toolsData.tools.filter(t => t.knowledgebase).length,
 | 
						||
                'Open Source': toolsData.tools.filter(t => t.license && t.license !== 'Proprietary').length
 | 
						||
            };
 | 
						||
            
 | 
						||
            return stats;
 | 
						||
        }
 | 
						||
 | 
						||
        // Bulk operations
 | 
						||
        function toggleBulkPanel() {
 | 
						||
            const panel = document.getElementById('bulkPanel');
 | 
						||
            panel.classList.toggle('active');
 | 
						||
        }
 | 
						||
 | 
						||
        function bulkRemoveTags() {
 | 
						||
            const tags = document.getElementById('bulkRemoveTagsInput').value
 | 
						||
                .split(',').map(t => t.trim()).filter(t => t);
 | 
						||
            
 | 
						||
            if (tags.length === 0) {
 | 
						||
                updateStatus('Please enter tags to remove');
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!confirm(`Remove tags "${tags.join(', ')}" from all tools?`)) return;
 | 
						||
            
 | 
						||
            let count = 0;
 | 
						||
            toolsData.tools.forEach(tool => {
 | 
						||
                if (tool.tags) {
 | 
						||
                    const before = tool.tags.length;
 | 
						||
                    tool.tags = tool.tags.filter(tag => !tags.includes(tag));
 | 
						||
                    if (tool.tags.length < before) count++;
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            updateStatus(`Removed tags from ${count} tools`);
 | 
						||
            document.getElementById('bulkRemoveTagsInput').value = '';
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        function bulkRenameTags() {
 | 
						||
            const fromTag = document.getElementById('bulkRenameFrom').value.trim();
 | 
						||
            const toTag = document.getElementById('bulkRenameTo').value.trim();
 | 
						||
            
 | 
						||
            if (!fromTag || !toTag) {
 | 
						||
                updateStatus('Please enter both "from" and "to" tags');
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!confirm(`Rename tag "${fromTag}" to "${toTag}" in all tools?`)) return;
 | 
						||
            
 | 
						||
            let count = 0;
 | 
						||
            toolsData.tools.forEach(tool => {
 | 
						||
                if (tool.tags && tool.tags.includes(fromTag)) {
 | 
						||
                    const index = tool.tags.indexOf(fromTag);
 | 
						||
                    tool.tags[index] = toTag;
 | 
						||
                    count++;
 | 
						||
                }
 | 
						||
            });
 | 
						||
            
 | 
						||
            updateStatus(`Renamed tag in ${count} tools`);
 | 
						||
            document.getElementById('bulkRenameFrom').value = '';
 | 
						||
            document.getElementById('bulkRenameTo').value = '';
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        function bulkAssignRelatedConcepts() {
 | 
						||
            const concepts = document.getElementById('bulkConceptsInput').value
 | 
						||
                .split(',').map(t => t.trim()).filter(t => t);
 | 
						||
            
 | 
						||
            if (concepts.length === 0) {
 | 
						||
                updateStatus('Please enter concepts to assign');
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!confirm(`Assign concepts "${concepts.join(', ')}" to all tools?`)) return;
 | 
						||
            
 | 
						||
            const selectedTools = getSelectedTools();
 | 
						||
            selectedTools.forEach(tool => {
 | 
						||
                if (!tool.related_concepts) tool.related_concepts = [];
 | 
						||
                concepts.forEach(concept => {
 | 
						||
                    if (!tool.related_concepts.includes(concept)) {
 | 
						||
                        tool.related_concepts.push(concept);
 | 
						||
                    }
 | 
						||
                });
 | 
						||
            });
 | 
						||
            
 | 
						||
            updateStatus(`Assigned concepts to ${selectedTools.length} tools`);
 | 
						||
            document.getElementById('bulkConceptsInput').value = '';
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        function bulkAssignRelatedSoftware() {
 | 
						||
            const software = document.getElementById('bulkSoftwareInput').value
 | 
						||
                .split(',').map(t => t.trim()).filter(t => t);
 | 
						||
            
 | 
						||
            if (software.length === 0) {
 | 
						||
                updateStatus('Please enter software to assign');
 | 
						||
                return;
 | 
						||
            }
 | 
						||
            
 | 
						||
            if (!confirm(`Assign software "${software.join(', ')}" to all tools?`)) return;
 | 
						||
            
 | 
						||
            const selectedTools = getSelectedTools();
 | 
						||
            selectedTools.forEach(tool => {
 | 
						||
                if (!tool.related_software) tool.related_software = [];
 | 
						||
                software.forEach(sw => {
 | 
						||
                    if (!tool.related_software.includes(sw)) {
 | 
						||
                        tool.related_software.push(sw);
 | 
						||
                    }
 | 
						||
                });
 | 
						||
            });
 | 
						||
            
 | 
						||
            updateStatus(`Assigned software to ${selectedTools.length} tools`);
 | 
						||
            document.getElementById('bulkSoftwareInput').value = '';
 | 
						||
            renderTools();
 | 
						||
        }
 | 
						||
 | 
						||
        function getSelectedTools() {
 | 
						||
            // For now, return all tools. Could be enhanced to support multi-selection
 | 
						||
            return toolsData.tools;
 | 
						||
        }
 | 
						||
 | 
						||
        // YAML operations
 | 
						||
        function importYAML(event) {
 | 
						||
            const file = event.target.files[0];
 | 
						||
            if (!file) return;
 | 
						||
            
 | 
						||
            const reader = new FileReader();
 | 
						||
            reader.onload = function(e) {
 | 
						||
                try {
 | 
						||
                    const yamlContent = e.target.result;
 | 
						||
                    const parsed = jsyaml.load(yamlContent);
 | 
						||
                    
 | 
						||
                    if (!parsed || !parsed.tools || !Array.isArray(parsed.tools)) {
 | 
						||
                        alert('Invalid YAML format: missing or invalid tools array');
 | 
						||
                        return;
 | 
						||
                    }
 | 
						||
                    
 | 
						||
                    // Show import summary and confirm
 | 
						||
                    const importSummary = `
 | 
						||
Import Summary:
 | 
						||
• ${parsed.tools.length} tools
 | 
						||
• ${parsed.tools.filter(t => t.type === 'software').length} software
 | 
						||
• ${parsed.tools.filter(t => t.type === 'method').length} methods  
 | 
						||
• ${parsed.tools.filter(t => t.type === 'concept').length} concepts
 | 
						||
• ${parsed.tools.filter(t => t.knowledgebase).length} with knowledgebase
 | 
						||
 | 
						||
This will replace all current data. Continue?`;
 | 
						||
 | 
						||
                    if (!confirm(importSummary)) {
 | 
						||
                        return;
 | 
						||
                    }
 | 
						||
                    
 | 
						||
                    // Import the data
 | 
						||
                    toolsData = parsed;
 | 
						||
                    
 | 
						||
                    // Ensure required structure exists
 | 
						||
                    if (!toolsData.domains) toolsData.domains = [];
 | 
						||
                    if (!toolsData.phases) toolsData.phases = [];
 | 
						||
                    if (!toolsData['domain-agnostic-software']) toolsData['domain-agnostic-software'] = [];
 | 
						||
                    if (!toolsData.scenarios) toolsData.scenarios = [];
 | 
						||
                    if (!toolsData.skill_levels) toolsData.skill_levels = {};
 | 
						||
                    
 | 
						||
                    populateFilters();
 | 
						||
                    
 | 
						||
                    // Check for validation errors after import
 | 
						||
                    updateStatus('Validating imported data...');
 | 
						||
                    
 | 
						||
                    const validationErrors = [];
 | 
						||
                    toolsData.tools.forEach((tool, index) => {
 | 
						||
                        const toolErrors = validateTool(tool, index);
 | 
						||
                        validationErrors.push(...toolErrors);
 | 
						||
                    });
 | 
						||
                    
 | 
						||
                    // Check for duplicate names
 | 
						||
                    const nameMap = new Map();
 | 
						||
                    toolsData.tools.forEach((tool, index) => {
 | 
						||
                        if (tool.name) {
 | 
						||
                            if (nameMap.has(tool.name)) {
 | 
						||
                                validationErrors.push(`Duplicate name "${tool.name}" found at indices ${nameMap.get(tool.name)} and ${index}`);
 | 
						||
                            } else {
 | 
						||
                                nameMap.set(tool.name, index);
 | 
						||
                            }
 | 
						||
                        }
 | 
						||
                    });
 | 
						||
                    
 | 
						||
                    if (validationErrors.length > 0) {
 | 
						||
                        // Show tools with validation issues
 | 
						||
                        showInvalidToolsFilter(validationErrors.length);
 | 
						||
                    } else {
 | 
						||
                        // Everything is valid
 | 
						||
                        hideValidationErrors();
 | 
						||
                        renderTools();
 | 
						||
                        updateStatus(`✅ Successfully imported ${toolsData.tools.length} tools - all valid`);
 | 
						||
                    }
 | 
						||
                    
 | 
						||
                } catch (error) {
 | 
						||
                    alert('Error parsing YAML: ' + error.message);
 | 
						||
                    updateStatus('❌ YAML parsing failed');
 | 
						||
                }
 | 
						||
            };
 | 
						||
            reader.readAsText(file);
 | 
						||
        }
 | 
						||
 | 
						||
        function exportYAML() {
 | 
						||
            // Check for validation errors before export
 | 
						||
            const allErrors = [];
 | 
						||
            toolsData.tools.forEach((tool, index) => {
 | 
						||
                const toolErrors = validateTool(tool, index);
 | 
						||
                allErrors.push(...toolErrors);
 | 
						||
            });
 | 
						||
            
 | 
						||
            if (allErrors.length > 0) {
 | 
						||
                const confirmExport = confirm(`⚠️ Warning: Found ${allErrors.length} validation errors in the database.\n\nExporting this data may cause issues when importing into the web application.\n\nWould you like to:\n• Click OK to export anyway\n• Click Cancel to fix errors first (recommended)`);
 | 
						||
                
 | 
						||
                if (!confirmExport) {
 | 
						||
                    // Set filter to show invalid tools and highlight the issues
 | 
						||
                    showInvalidToolsFilter(allErrors.length);
 | 
						||
                    return;
 | 
						||
                }
 | 
						||
            }
 | 
						||
            
 | 
						||
            try {
 | 
						||
                // Process tools to ensure proper description formatting
 | 
						||
                const processedData = {
 | 
						||
                    ...toolsData,
 | 
						||
                    tools: toolsData.tools.map(tool => {
 | 
						||
                        const processedTool = { ...tool };
 | 
						||
                        
 | 
						||
                        // Format description with folded style for multiline
 | 
						||
                        if (processedTool.description && processedTool.description.length > 60) {
 | 
						||
                            // Keep the description as is, jsyaml will handle the formatting
 | 
						||
                        }
 | 
						||
                        
 | 
						||
                        // Clean up empty arrays and null values
 | 
						||
                        Object.keys(processedTool).forEach(key => {
 | 
						||
                            if (Array.isArray(processedTool[key]) && processedTool[key].length === 0) {
 | 
						||
                                delete processedTool[key];
 | 
						||
                            }
 | 
						||
                            if (processedTool[key] === null || processedTool[key] === undefined || processedTool[key] === '') {
 | 
						||
                                delete processedTool[key];
 | 
						||
                            }
 | 
						||
                        });
 | 
						||
                        
 | 
						||
                        return processedTool;
 | 
						||
                    })
 | 
						||
                };
 | 
						||
                
 | 
						||
                const yamlContent = jsyaml.dump(processedData, {
 | 
						||
                    indent: 2,
 | 
						||
                    lineWidth: 80,
 | 
						||
                    noRefs: true,
 | 
						||
                    sortKeys: false,
 | 
						||
                    styles: {
 | 
						||
                        '!!str': 'fold'  // Use folded style for long strings
 | 
						||
                    },
 | 
						||
                    replacer: (key, value) => {
 | 
						||
                        // Handle description field specially
 | 
						||
                        if (key === 'description' && typeof value === 'string' && value.length > 60) {
 | 
						||
                            return value;
 | 
						||
                        }
 | 
						||
                        return value;
 | 
						||
                    }
 | 
						||
                });
 | 
						||
                
 | 
						||
                // Post-process to ensure descriptions use >- format
 | 
						||
                const lines = yamlContent.split('\n');
 | 
						||
                const processedLines = [];
 | 
						||
                let inDescription = false;
 | 
						||
                
 | 
						||
                for (let i = 0; i < lines.length; i++) {
 | 
						||
                    const line = lines[i];
 | 
						||
                    
 | 
						||
                    if (line.trim().startsWith('description:')) {
 | 
						||
                        if (line.includes('"') || line.includes("'")) {
 | 
						||
                            // Replace quoted description with folded style
 | 
						||
                            const indent = line.match(/^(\s*)/)[1];
 | 
						||
                            processedLines.push(`${indent}description: >-`);
 | 
						||
                            
 | 
						||
                            // Extract the description content
 | 
						||
                            let descContent = line.substring(line.indexOf(':') + 1).trim();
 | 
						||
                            if (descContent.startsWith('"') || descContent.startsWith("'")) {
 | 
						||
                                descContent = descContent.slice(1, -1);
 | 
						||
                            }
 | 
						||
                            
 | 
						||
                            // Split and reformat the description
 | 
						||
                            const words = descContent.split(' ');
 | 
						||
                            let currentLine = '';
 | 
						||
                            const descIndent = indent + '  ';
 | 
						||
                            
 | 
						||
                            words.forEach(word => {
 | 
						||
                                if (currentLine.length + word.length > 76) {
 | 
						||
                                    if (currentLine) {
 | 
						||
                                        processedLines.push(descIndent + currentLine.trim());
 | 
						||
                                    }
 | 
						||
                                    currentLine = word + ' ';
 | 
						||
                                } else {
 | 
						||
                                    currentLine += word + ' ';
 | 
						||
                                }
 | 
						||
                            });
 | 
						||
                            
 | 
						||
                            if (currentLine.trim()) {
 | 
						||
                                processedLines.push(descIndent + currentLine.trim());
 | 
						||
                            }
 | 
						||
                        } else {
 | 
						||
                            processedLines.push(line);
 | 
						||
                        }
 | 
						||
                    } else {
 | 
						||
                        processedLines.push(line);
 | 
						||
                    }
 | 
						||
                }
 | 
						||
                
 | 
						||
                const finalYaml = processedLines.join('\n');
 | 
						||
                
 | 
						||
                const blob = new Blob([finalYaml], { type: 'text/yaml' });
 | 
						||
                const url = URL.createObjectURL(blob);
 | 
						||
                const a = document.createElement('a');
 | 
						||
                a.href = url;
 | 
						||
                a.download = 'tools.yaml';
 | 
						||
                a.click();
 | 
						||
                URL.revokeObjectURL(url);
 | 
						||
                
 | 
						||
                updateStatus(allErrors.length > 0 ? 
 | 
						||
                    `⚠️ YAML exported with ${allErrors.length} validation errors` : 
 | 
						||
                    '✅ YAML exported successfully'
 | 
						||
                );
 | 
						||
            } catch (error) {
 | 
						||
                alert('Error exporting YAML: ' + error.message);
 | 
						||
            }
 | 
						||
        }
 | 
						||
 | 
						||
        // Initialize on load
 | 
						||
        document.addEventListener('DOMContentLoaded', init);
 | 
						||
 | 
						||
        // Keyboard shortcuts
 | 
						||
        document.addEventListener('keydown', function(event) {
 | 
						||
            // Ctrl/Cmd + S to save
 | 
						||
            if ((event.ctrlKey || event.metaKey) && event.key === 's') {
 | 
						||
                event.preventDefault();
 | 
						||
                saveTool();
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Ctrl/Cmd + N to add new tool
 | 
						||
            if ((event.ctrlKey || event.metaKey) && event.key === 'n') {
 | 
						||
                event.preventDefault();
 | 
						||
                addNewTool();
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Ctrl/Cmd + E to export
 | 
						||
            if ((event.ctrlKey || event.metaKey) && event.key === 'e') {
 | 
						||
                event.preventDefault();
 | 
						||
                exportYAML();
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Alt + V to validate all (changed from Ctrl+V to avoid paste conflict)
 | 
						||
            if (event.altKey && event.key === 'v') {
 | 
						||
                event.preventDefault();
 | 
						||
                validateAllTools();
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Escape to clear selection/form
 | 
						||
            if (event.key === 'Escape') {
 | 
						||
                clearSelection();
 | 
						||
                clearForm();
 | 
						||
            }
 | 
						||
            
 | 
						||
            // Ctrl/Cmd + F to focus search
 | 
						||
            if ((event.ctrlKey || event.metaKey) && event.key === 'f') {
 | 
						||
                event.preventDefault();
 | 
						||
                document.getElementById('searchInput').focus();
 | 
						||
            }
 | 
						||
        });
 | 
						||
 | 
						||
        // Hide autocomplete when clicking outside
 | 
						||
        document.addEventListener('click', function(event) {
 | 
						||
            if (!event.target.closest('.autocomplete-container')) {
 | 
						||
                document.querySelectorAll('.autocomplete-dropdown').forEach(dropdown => {
 | 
						||
                    dropdown.style.display = 'none';
 | 
						||
                });
 | 
						||
            }
 | 
						||
        });
 | 
						||
    </script>
 | 
						||
</body>
 | 
						||
</html> |