make icons

This commit is contained in:
overcuriousity
2025-07-19 23:15:57 +02:00
parent 8cccb0f4a9
commit c01a73bbb7
8 changed files with 632 additions and 281 deletions

View File

@@ -146,10 +146,26 @@
box-shadow: 0 8px 15px rgba(0,0,0,0.15);
}
.tool-card.method {
border-left: 4px solid #9b59b6;
background: linear-gradient(135deg, #f8f9fa 0%, #f4f1ff 100%);
}
.tool-card.software {
border-left: 4px solid #3498db;
}
.tool-card h3 {
color: #2c3e50;
margin-bottom: 10px;
font-size: 1.3em;
display: flex;
align-items: center;
gap: 8px;
}
.tool-icon {
font-size: 1.4em;
}
.tool-card p {
@@ -169,6 +185,12 @@
margin: 2px;
}
.tag.method-tag {
background: #e8e4ff;
color: #9b59b6;
font-weight: bold;
}
.tag.domain-agnostic {
background: #e8f5e8;
color: #27ae60;
@@ -426,6 +448,42 @@
margin: 10px 0;
border: 1px solid #c3e6cb;
}
.type-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.75em;
font-weight: bold;
text-transform: uppercase;
margin-left: 10px;
}
.type-software {
background: #3498db;
color: white;
}
.type-method {
background: #9b59b6;
color: white;
}
.conditional-fields {
transition: opacity 0.3s ease;
}
.conditional-fields.disabled {
opacity: 0.5;
pointer-events: none;
}
.icon-input {
font-size: 1.5em;
text-align: center;
padding: 10px;
width: 80px;
}
</style>
</head>
<body>
@@ -458,6 +516,14 @@
<div class="stat-number" id="totalTools">0</div>
<div class="stat-label">Total Tools</div>
</div>
<div class="stat-card">
<div class="stat-number" id="softwareCount">0</div>
<div class="stat-label">Software</div>
</div>
<div class="stat-card">
<div class="stat-number" id="methodCount">0</div>
<div class="stat-label">Methods</div>
</div>
<div class="stat-card">
<div class="stat-number" id="totalDomains">0</div>
<div class="stat-label">Domains</div>
@@ -496,7 +562,7 @@
<!-- Tools Tab -->
<div id="tools" class="tab-content">
<input type="text" class="search-bar" id="searchBar" placeholder="🔍 Search tools by name, description, tags, domains, phases, or domain-agnostic categories..." onkeyup="filterTools()" />
<input type="text" class="search-bar" id="searchBar" placeholder="🔍 Search tools by name, description, tags, domains, phases, or type..." onkeyup="filterTools()" />
<div class="tools-grid" id="toolsGrid"></div>
</div>
@@ -506,11 +572,31 @@
<h3 id="editorTitle">Add New Tool</h3>
<div id="messageArea"></div>
<form id="toolForm">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div style="display: grid; grid-template-columns: 1fr 1fr 100px; gap: 20px;">
<div class="form-group">
<label for="toolName">Tool Name *</label>
<input type="text" id="toolName" required />
</div>
<div class="form-group">
<label for="toolType">Type *</label>
<select id="toolType" required onchange="handleTypeChange()">
<option value="">Select Type</option>
<option value="software">Software</option>
<option value="method">Method</option>
</select>
</div>
<div class="form-group">
<label for="toolIcon">Icon <small style="color: #7f8c8d; cursor: pointer;" onclick="showIconSuggestions()" title="Click for suggestions">💡 suggestions</small></label>
<input type="text" id="toolIcon" class="icon-input" placeholder="🔧" maxlength="2" />
</div>
</div>
<div class="form-group">
<label for="description">Description *</label>
<textarea id="description" rows="3" required></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="skillLevel">Skill Level *</label>
<select id="skillLevel" required>
@@ -522,74 +608,75 @@
<option value="expert">Expert</option>
</select>
</div>
</div>
<div class="form-group">
<label for="description">Description *</label>
<textarea id="description" rows="3" required></textarea>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="url">URL</label>
<input type="url" id="url" />
</div>
<div class="form-group">
<label for="projectUrl">Project URL</label>
<input type="url" id="projectUrl" />
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="license">License</label>
<input type="text" id="license" />
<!-- Software-specific fields -->
<div id="softwareFields" class="conditional-fields">
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="projectUrl">Project URL</label>
<input type="url" id="projectUrl" />
</div>
<div class="form-group">
<label for="license">License</label>
<input type="text" id="license" />
</div>
<div class="form-group">
<label for="accessType">Access Type</label>
<select id="accessType">
<option value="">Select Type</option>
<option value="download">Download</option>
<option value="server-based">CC24-Server</option>
<option value="commercial">Commercial</option>
<option value="built-in">Built-in</option>
</select>
</div>
</div>
<div class="form-group">
<label for="accessType">Access Type</label>
<select id="accessType">
<option value="">Select Type</option>
<option value="download">Download</option>
<option value="server-based">CC24-Server</option>
<option value="commercial">Commercial</option>
<option value="OS">Operating System</option>
</select>
</div>
<div class="form-group">
<label for="statusUrl">Status URL</label>
<input type="url" id="statusUrl" />
</div>
</div>
<div class="form-group">
<div class="checkbox-item">
<input type="checkbox" id="knowledgebase" />
<label for="knowledgebase">📚 Knowledgebase Tool</label>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div class="form-group">
<label for="statusUrl">Status URL</label>
<input type="url" id="statusUrl" />
</div>
<div class="form-group">
<div class="checkbox-item">
<input type="checkbox" id="knowledgebase" />
<label for="knowledgebase">📚 Knowledgebase Tool</label>
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Platforms</label>
<div class="checkbox-group" id="platformsCheckbox">
<div class="checkbox-item">
<input type="checkbox" id="platform-windows" value="Windows">
<label for="platform-windows">Windows</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-linux" value="Linux">
<label for="platform-linux">Linux</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-macos" value="macOS">
<label for="platform-macos">macOS</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-web" value="Web">
<label for="platform-web">Web</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-os" value="OS">
<label for="platform-os">Operating System</label>
<div class="form-group">
<label>Platforms</label>
<div class="checkbox-group" id="platformsCheckbox">
<div class="checkbox-item">
<input type="checkbox" id="platform-windows" value="Windows">
<label for="platform-windows">Windows</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-linux" value="Linux">
<label for="platform-linux">Linux</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-macos" value="macOS">
<label for="platform-macos">macOS</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-web" value="Web">
<label for="platform-web">Web</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-os" value="OS">
<label for="platform-os">Operating System</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="platform-hardware" value="Hardware">
<label for="platform-hardware">Hardware</label>
</div>
</div>
</div>
</div>
@@ -633,6 +720,8 @@
<div style="margin: 20px 0;">
<button class="btn" onclick="selectAllTools()">Select All</button>
<button class="btn" onclick="selectByType('software')">Select Software</button>
<button class="btn" onclick="selectByType('method')">Select Methods</button>
<button class="btn" onclick="clearSelection()">Clear Selection</button>
<span id="selectionCount" style="margin-left: 20px; font-weight: bold;">0 selected</span>
</div>
@@ -643,11 +732,14 @@
<button class="btn btn-warning" onclick="bulkUpdatePhases()">Update Phases</button>
<button class="btn btn-warning" onclick="bulkUpdateDomainAgnostic()">Update Domain-Agnostic</button>
<button class="btn btn-warning" onclick="bulkUpdateTags()">Update Tags</button>
<button class="btn btn-warning" onclick="bulkUpdateType()">Update Type</button>
<button class="btn btn-warning" onclick="bulkUpdateIcons()">🎨 Update Icons</button>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
<button class="btn" onclick="bulkSetKnowledgebase(true)">📚 Set as Knowledgebase</button>
<button class="btn" onclick="bulkSetKnowledgebase(false)">📖 Remove Knowledgebase</button>
<button class="btn" onclick="bulkClearField('icon')">🗑️ Clear Icons</button>
<button class="btn" onclick="bulkClearField('tags')">🗑️ Clear All Tags</button>
<button class="btn" onclick="bulkClearField('domains')">🗑️ Clear All Domains</button>
</div>
@@ -724,6 +816,25 @@
}
}
function handleTypeChange() {
const type = document.getElementById('toolType').value;
const softwareFields = document.getElementById('softwareFields');
if (type === 'method') {
softwareFields.classList.add('disabled');
// Clear software-specific fields for methods
document.getElementById('projectUrl').value = '';
document.getElementById('license').value = '';
document.getElementById('accessType').value = '';
document.getElementById('statusUrl').value = '';
document.getElementById('knowledgebase').checked = false;
// Clear platform checkboxes
document.querySelectorAll('#platformsCheckbox input').forEach(cb => cb.checked = false);
} else {
softwareFields.classList.remove('disabled');
}
}
function showMessage(message, type = 'success') {
const messageArea = document.getElementById('messageArea');
const className = type === 'error' ? 'error-message' : 'success-message';
@@ -757,24 +868,56 @@
}
function loadSampleData() {
// This would load from your existing YAML data
// For brevity, I'll just show the structure
try {
const sampleData = {
tools: [], // Your existing tools
domains: [], // Your existing domains
phases: [], // Your existing phases
"domain-agnostic-software": [
tools: [
{
id: "collaboration-general",
name: "Übergreifend & Kollaboration",
description: "Cross-cutting tools and collaboration platforms"
name: "Autopsy",
icon: "📱",
type: "software",
description: "The leading open-source digital forensics platform.",
domains: ["incident-response", "law-enforcement"],
phases: ["examination", "analysis"],
platforms: ["Windows", "Linux"],
skillLevel: "intermediate",
accessType: "download",
url: "https://www.autopsy.com/",
projectUrl: "",
license: "Apache 2.0",
knowledgebase: false,
tags: ["gui", "filesystem", "timeline-analysis", "carving"]
},
{
id: "specific-os",
name: "Betriebssysteme",
description: "Operating Systems which focus on forensics"
name: "Live Memory Acquisition Procedure",
icon: "🧠",
type: "method",
description: "Standardized procedure for forensically sound memory acquisition.",
domains: ["incident-response", "law-enforcement"],
phases: ["data-collection"],
platforms: [],
skillLevel: "advanced",
accessType: null,
url: "https://www.nist.gov/publications/guide-integrating-forensic-techniques-incident-response",
projectUrl: null,
license: null,
knowledgebase: false,
tags: ["memory-acquisition", "volatile-evidence", "procedure"]
}
],
domains: [
{ id: "incident-response", name: "Incident Response & Breach Investigation" },
{ id: "law-enforcement", name: "Law Enforcement & Criminal Investigation" },
{ id: "malware-analysis", name: "Malware Analysis & Reverse Engineering" }
],
phases: [
{ id: "data-collection", name: "Data Collection", description: "Imaging, Acquisition, Remote Collection Tools" },
{ id: "examination", name: "Examination", description: "Parsing, Extraction, Initial Analysis Tools" },
{ id: "analysis", name: "Analysis", description: "Deep Analysis, Correlation, Visualization Tools" },
{ id: "reporting", name: "Reporting", description: "Documentation, Visualization, Presentation Tools" }
],
"domain-agnostic-software": [
{ id: "collaboration-general", name: "Collaboration & General", description: "Cross-cutting tools and collaboration platforms" },
{ id: "specific-os", name: "Operating Systems", description: "Operating Systems which focus on forensics" }
]
};
yamlData = sampleData;
@@ -808,6 +951,12 @@
const tools = yamlData.tools;
document.getElementById('totalTools').textContent = tools.length;
const softwareCount = tools.filter(tool => tool.type === 'software').length;
const methodCount = tools.filter(tool => tool.type === 'method').length;
document.getElementById('softwareCount').textContent = softwareCount;
document.getElementById('methodCount').textContent = methodCount;
document.getElementById('totalDomains').textContent = yamlData.domains ? yamlData.domains.length : 0;
document.getElementById('totalPhases').textContent = yamlData.phases ? yamlData.phases.length : 0;
document.getElementById('totalDomainAgnostic').textContent = yamlData['domain-agnostic-software'] ? yamlData['domain-agnostic-software'].length : 0;
@@ -973,11 +1122,12 @@
function createToolCard(tool, index) {
const card = document.createElement('div');
card.className = 'tool-card';
card.className = `tool-card ${tool.type || 'software'}`;
const skillClass = `skill-${tool.skillLevel || 'intermediate'}`;
const tags = (tool.tags || []).map(tag => `<span class="tag">${tag}</span>`).join('');
const knowledgebaseIndicator = tool.knowledgebase ? '<span class="tag" style="background: #e8f5e8; color: #27ae60; font-weight: bold;">📚 Knowledgebase</span>' : '';
const typeIndicator = `<span class="type-badge type-${tool.type || 'software'}">${tool.type || 'software'}</span>`;
// Add domain-agnostic indicators
const domainAgnosticTags = (tool['domain-agnostic-software'] || []).map(cat => {
@@ -986,7 +1136,11 @@
}).join('');
card.innerHTML = `
<h3>${tool.name}</h3>
<h3>
${tool.icon ? `<span class="tool-icon">${tool.icon}</span>` : ''}
${tool.name}
${typeIndicator}
</h3>
<div style="margin: 5px 0;">
<div class="skill-badge ${skillClass}">${tool.skillLevel || 'intermediate'}</div>
${knowledgebaseIndicator}
@@ -1004,16 +1158,21 @@
function createBulkToolCard(tool, index) {
const card = document.createElement('div');
card.className = 'tool-card';
card.className = `tool-card ${tool.type || 'software'}`;
const skillClass = `skill-${tool.skillLevel || 'intermediate'}`;
const isSelected = selectedTools.has(index);
const knowledgebaseIndicator = tool.knowledgebase ? '<span class="tag" style="background: #e8f5e8; color: #27ae60; font-weight: bold; margin-left: 10px;">📚 KB</span>' : '';
const typeIndicator = `<span class="type-badge type-${tool.type || 'software'}" style="margin-left: 10px;">${tool.type || 'software'}</span>`;
card.innerHTML = `
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 10px;">
<input type="checkbox" ${isSelected ? 'checked' : ''} onchange="toggleToolSelection(${index})" />
<h3 style="margin: 0;">${tool.name}</h3>
<h3 style="margin: 0; display: flex; align-items: center; gap: 8px;">
${tool.icon ? `<span class="tool-icon">${tool.icon}</span>` : ''}
${tool.name}
</h3>
${typeIndicator}
${knowledgebaseIndicator}
</div>
<div class="skill-badge ${skillClass}">${tool.skillLevel || 'intermediate'}</div>
@@ -1046,6 +1205,7 @@
const searchableText = [
tool.name || '',
tool.description || '',
tool.type || '',
...(tool.tags || []),
...(tool.domains || []),
...(tool.phases || []),
@@ -1077,6 +1237,8 @@
// Populate form fields
document.getElementById('toolName').value = tool.name || '';
document.getElementById('toolType').value = tool.type || 'software';
document.getElementById('toolIcon').value = tool.icon || '';
document.getElementById('description').value = tool.description || '';
document.getElementById('skillLevel').value = tool.skillLevel || '';
document.getElementById('url').value = tool.url || '';
@@ -1086,6 +1248,9 @@
document.getElementById('statusUrl').value = tool.statusUrl || '';
document.getElementById('knowledgebase').checked = tool.knowledgebase || false;
// Handle conditional fields
handleTypeChange();
// Set checkboxes
setCheckboxValues('#platformsCheckbox input', tool.platforms || []);
setCheckboxValues('#domainsCheckbox input', tool.domains || []);
@@ -1149,36 +1314,70 @@
yamlData.tools = [];
}
const toolType = document.getElementById('toolType').value;
const tool = {
name: document.getElementById('toolName').value,
type: toolType,
description: document.getElementById('description').value,
domains: getCheckedValues('#domainsCheckbox input:checked'),
phases: getCheckedValues('#phasesCheckbox input:checked'),
platforms: getCheckedValues('#platformsCheckbox input:checked'),
'domain-agnostic-software': getCheckedValues('#domainAgnosticCheckbox input:checked'),
skillLevel: document.getElementById('skillLevel').value,
accessType: document.getElementById('accessType').value,
url: document.getElementById('url').value,
projectUrl: document.getElementById('projectUrl').value,
license: document.getElementById('license').value,
knowledgebase: document.getElementById('knowledgebase').checked,
tags: getTags()
};
// Add icon if provided
const icon = document.getElementById('toolIcon').value.trim();
if (icon) {
tool.icon = icon;
}
// Add software-specific fields
if (toolType === 'software') {
tool.platforms = getCheckedValues('#platformsCheckbox input:checked');
tool.accessType = document.getElementById('accessType').value;
tool.projectUrl = document.getElementById('projectUrl').value;
tool.license = document.getElementById('license').value;
tool.knowledgebase = document.getElementById('knowledgebase').checked;
const statusUrl = document.getElementById('statusUrl').value;
if (statusUrl) {
tool.statusUrl = statusUrl;
}
} else {
// For methods, set appropriate defaults
tool.platforms = [];
tool.accessType = null;
tool.projectUrl = null;
tool.license = null;
tool.knowledgebase = false;
}
// Add domain-agnostic software if selected
const domainAgnostic = getCheckedValues('#domainAgnosticCheckbox input:checked');
if (domainAgnostic.length > 0) {
tool['domain-agnostic-software'] = domainAgnostic;
} else {
tool['domain-agnostic-software'] = null;
}
// Clean up empty arrays and null values
Object.keys(tool).forEach(key => {
if (Array.isArray(tool[key]) && tool[key].length === 0) {
delete tool[key];
if (key === 'platforms' && toolType === 'method') {
tool[key] = []; // Keep empty array for methods
} else {
delete tool[key];
}
} else if (tool[key] === '' || tool[key] === null) {
delete tool[key];
if ((key === 'accessType' || key === 'projectUrl' || key === 'license') && toolType === 'method') {
tool[key] = null; // Keep null for methods
} else {
delete tool[key];
}
}
});
const statusUrl = document.getElementById('statusUrl').value;
if (statusUrl) {
tool.statusUrl = statusUrl;
}
if (currentEditingIndex >= 0) {
yamlData.tools[currentEditingIndex] = tool;
showMessage('Tool updated successfully!');
@@ -1213,6 +1412,7 @@
document.getElementById('tagContainer').querySelectorAll('.removable-tag').forEach(tag => tag.remove());
document.getElementById('editorTitle').textContent = 'Add New Tool';
document.getElementById('deleteBtn').style.display = 'none';
document.getElementById('softwareFields').classList.remove('disabled');
currentEditingIndex = -1;
}
@@ -1251,6 +1451,18 @@
}
}
function selectByType(type) {
if (yamlData && yamlData.tools) {
yamlData.tools.forEach((tool, index) => {
if (tool.type === type) {
selectedTools.add(index);
}
});
updateSelectionCount();
renderBulkGrid();
}
}
function clearSelection() {
selectedTools.clear();
updateSelectionCount();
@@ -1261,6 +1473,111 @@
document.getElementById('selectionCount').textContent = `${selectedTools.size} selected`;
}
function showIconSuggestions() {
const suggestions = `DFIR Tool Icons by Operational Mode:
📦 Downloaded/Installed 🌐 Web Application ☁️ Cloud Service
🖥️ Operating System ⌨️ Command Line 📡 Server/Self-hosted
🔧 Hardware Tool 💰 Commercial ⚙️ Built-in/System
📱 Mobile Application 🔗 API/Library 📋 Method/Procedure
🖲️ Remote Access 💻 Desktop GUI 🛠️ Utility/Helper
🏢 Enterprise Platform 🔓 Open Source 🎯 Specialized Tool
📊 Analysis Platform 🗄️ Database/Storage 🔄 Processing Engine
Most common combinations:
📦 + 🔓 = Open Source Desktop Software
🌐 + ☁️ = Cloud Web Application
🖥️ + 🔓 = Forensic Live OS
⌨️ + 🔧 = Command Line Utility
📋 + 🎯 = Specialized Method
Click any emoji to copy it, then paste into the icon field.`;
alert(suggestions);
}
function bulkUpdateIcons() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
// Show operational mode-focused icon suggestions
const suggestedIcons = `
DFIR Tool Icons by Operational Mode:
📦 Downloaded/Installed 🌐 Web Application ☁️ Cloud Service
🖥️ Operating System ⌨️ Command Line 📡 Server/Self-hosted
🔧 Hardware Tool 💰 Commercial ⚙️ Built-in/System
📱 Mobile Application 🔗 API/Library 📋 Method/Procedure
🖲️ Remote Access 💻 Desktop GUI 🛠️ Utility/Helper
🏢 Enterprise Platform 🔓 Open Source 🎯 Specialized Tool
📊 Analysis Platform 🗄️ Database/Storage 🔄 Processing Engine
Quick suggestions by access type:
• Downloaded Software: 📦
• Web Applications: 🌐
• Cloud Services: ☁️
• Operating Systems: 🖥️
• Command Line: ⌨️
• Hardware: 🔧
• Methods/Procedures: 📋`;
const icon = prompt(`Enter emoji icon (single character) or leave empty to remove:\n\n${suggestedIcons}`);
if (icon !== null) { // Allow empty string to remove icons
const trimmedIcon = icon.trim();
if (trimmedIcon.length > 2) {
showMessage('Icon should be a single emoji character', 'error');
return;
}
selectedTools.forEach(index => {
if (trimmedIcon === '') {
delete yamlData.tools[index].icon; // Remove icon field entirely
} else {
yamlData.tools[index].icon = trimmedIcon;
}
});
const action = trimmedIcon === '' ? 'removed icons from' : 'updated icons for';
showMessage(`Successfully ${action} ${selectedTools.size} tools`);
renderBulkGrid();
renderToolsGrid(); // Update tools view if visible
}
}
function bulkUpdateType() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
return;
}
const type = prompt('Enter type (software/method):');
if (type && ['software', 'method'].includes(type)) {
selectedTools.forEach(index => {
yamlData.tools[index].type = type;
// Handle method-specific cleanup
if (type === 'method') {
yamlData.tools[index].platforms = [];
yamlData.tools[index].accessType = null;
yamlData.tools[index].projectUrl = null;
yamlData.tools[index].license = null;
yamlData.tools[index].knowledgebase = false;
if (yamlData.tools[index].statusUrl) {
delete yamlData.tools[index].statusUrl;
}
}
});
showMessage(`Updated type for ${selectedTools.size} tools`);
updateStats();
renderBulkGrid();
}
}
function bulkUpdateSkillLevel() {
if (selectedTools.size === 0) {
showMessage('No tools selected', 'error');
@@ -1352,19 +1669,29 @@
return;
}
const action = value ? 'set as knowledgebase' : 'remove knowledgebase flag from';
if (!confirm(`Are you sure you want to ${action} ${selectedTools.size} selected tools?`)) {
// Only apply to software tools
const softwareTools = Array.from(selectedTools).filter(index =>
yamlData.tools[index].type === 'software'
);
if (softwareTools.length === 0) {
showMessage('No software tools selected (knowledgebase only applies to software)', 'error');
return;
}
selectedTools.forEach(index => {
const action = value ? 'set as knowledgebase' : 'remove knowledgebase flag from';
if (!confirm(`Are you sure you want to ${action} ${softwareTools.length} selected software tools?`)) {
return;
}
softwareTools.forEach(index => {
if (yamlData.tools[index]) {
yamlData.tools[index].knowledgebase = value;
}
});
const actionCompleted = value ? 'marked as knowledgebase' : 'removed knowledgebase flag from';
showMessage(`Successfully ${actionCompleted} ${selectedTools.size} tools`);
showMessage(`Successfully ${actionCompleted} ${softwareTools.length} software tools`);
updateStats();
renderBulkGrid();
@@ -1383,11 +1710,23 @@
selectedTools.forEach(index => {
if (yamlData.tools[index]) {
const tool = yamlData.tools[index];
const arrayFields = ['tags', 'domains', 'phases', 'platforms', 'domain-agnostic-software'];
if (arrayFields.includes(fieldName)) {
yamlData.tools[index][fieldName] = [];
if (fieldName === 'platforms' && tool.type === 'method') {
tool[fieldName] = []; // Keep empty array for methods
} else {
tool[fieldName] = [];
}
} else if (fieldName === 'icon') {
delete tool.icon; // Remove icon field entirely
} else {
yamlData.tools[index][fieldName] = '';
if ((fieldName === 'projectUrl' || fieldName === 'accessType' || fieldName === 'license') && tool.type === 'method') {
tool[fieldName] = null; // Keep null for methods
} else {
tool[fieldName] = '';
}
}
}
});
@@ -1399,6 +1738,9 @@
}
renderBulkGrid();
if (fieldName === 'icon') {
renderToolsGrid(); // Update tools view to reflect icon changes
}
}
function bulkDelete() {
@@ -1438,6 +1780,25 @@
if (!tool.name) validationResults.push(`❌ Tool ${index + 1}: Missing name`);
if (!tool.description) validationResults.push(`❌ Tool ${index + 1}: Missing description`);
if (!tool.skillLevel) validationResults.push(`❌ Tool ${index + 1}: Missing skillLevel`);
if (!tool.type) validationResults.push(`❌ Tool ${index + 1}: Missing type`);
if (tool.type && !['software', 'method'].includes(tool.type)) {
validationResults.push(`❌ Tool ${index + 1}: Invalid type (must be 'software' or 'method')`);
}
// Software-specific validation
if (tool.type === 'software') {
if (!tool.platforms || tool.platforms.length === 0) {
validationResults.push(`❌ Tool ${index + 1}: Software must have platforms`);
}
if (!tool.license) validationResults.push(`❌ Tool ${index + 1}: Software should have license`);
}
// Method-specific validation
if (tool.type === 'method') {
if (tool.platforms && tool.platforms.length > 0) {
validationResults.push(`⚠️ Tool ${index + 1}: Methods should not have platforms`);
}
}
});
}