main #11

Merged
mstoeck3 merged 66 commits from main into forensic-ai 2025-08-11 12:02:56 +00:00
4 changed files with 10123 additions and 10098 deletions
Showing only changes of commit 9ce2098439 - Show all commits

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
// src/components/AIQueryInterface.astro
import { getToolsData } from '../utils/dataService.js';
import { isToolHosted } from '../utils/toolHelpers.js';
const data = await getToolsData();
const tools = data.tools;

View File

@ -22,36 +22,26 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<script>
function createToolSlug(toolName) {
if (!toolName || typeof toolName !== 'string') {
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
return '';
// Import utility functions from shared client module instead of duplicating
async function loadUtilityFunctions() {
try {
const { createToolSlug, findToolByIdentifier, isToolHosted } = await import('../utils/clientUtils.js');
// Make functions available globally for backward compatibility
(window as any).createToolSlug = createToolSlug;
(window as any).findToolByIdentifier = findToolByIdentifier;
(window as any).isToolHosted = isToolHosted;
} catch (error) {
console.error('Failed to load utility functions:', error);
// Minimal fallback for critical functionality only
(window as any).createToolSlug = (toolName: string) => {
if (!toolName || typeof toolName !== 'string') return '';
return toolName.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
};
}
}
return toolName.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-') // Remove duplicate hyphens
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
}
function findToolByIdentifier(tools, identifier) {
if (!identifier || !Array.isArray(tools)) return undefined;
return tools.find(tool =>
tool.name === identifier ||
createToolSlug(tool.name) === identifier.toLowerCase()
);
}
function isToolHosted(tool) {
return tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
tool.projectUrl.trim() !== "";
}
function scrollToElement(element, options = {}) {
function scrollToElement(element: Element | null, options = {}) {
if (!element) return;
setTimeout(() => {
@ -67,17 +57,21 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
}, 100);
}
function scrollToElementById(elementId, options = {}) {
function scrollToElementById(elementId: string, options = {}) {
const element = document.getElementById(elementId);
if (element) {
scrollToElement(element, options);
}
}
function scrollToElementBySelector(selector, options = {}) {
function scrollToElementBySelector(selector: string, options = {}) {
const element = document.querySelector(selector);
if (element) {
scrollToElement(element, options);
}
}
function prioritizeSearchResults(tools, searchTerm) {
function prioritizeSearchResults(tools: any[], searchTerm: string) {
if (!searchTerm || !searchTerm.trim()) {
return tools;
}
@ -85,8 +79,8 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
const lowerSearchTerm = searchTerm.toLowerCase().trim();
return tools.sort((a, b) => {
const aTagsLower = (a.tags || []).map(tag => tag.toLowerCase());
const bTagsLower = (b.tags || []).map(tag => tag.toLowerCase());
const aTagsLower = (a.tags || []).map((tag: string) => tag.toLowerCase());
const bTagsLower = (b.tags || []).map((tag: string) => tag.toLowerCase());
const aExactTag = aTagsLower.includes(lowerSearchTerm);
const bExactTag = bTagsLower.includes(lowerSearchTerm);
@ -98,15 +92,16 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
});
}
(window as any).createToolSlug = createToolSlug;
(window as any).findToolByIdentifier = findToolByIdentifier;
(window as any).isToolHosted = isToolHosted;
// Set non-duplicated functions on window
(window as any).scrollToElement = scrollToElement;
(window as any).scrollToElementById = scrollToElementById;
(window as any).scrollToElementBySelector = scrollToElementBySelector;
(window as any).prioritizeSearchResults = prioritizeSearchResults;
document.addEventListener('DOMContentLoaded', () => {
// Load utility functions first
loadUtilityFunctions();
const THEME_KEY = 'dfir-theme';
function getSystemTheme() {
@ -117,12 +112,12 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
return localStorage.getItem(THEME_KEY) || 'auto';
}
function applyTheme(theme) {
function applyTheme(theme: string) {
const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
document.documentElement.setAttribute('data-theme', effectiveTheme);
}
function updateThemeToggle(theme) {
function updateThemeToggle(theme: string) {
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
button.setAttribute('data-current-theme', theme);
});
@ -192,7 +187,7 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
}
}
async function requireClientAuth(callback, returnUrl, context = 'general') {
async function requireClientAuth(callback: () => void, returnUrl: string, context = 'general') {
const authStatus = await checkClientAuth(context);
if (authStatus.authRequired && !authStatus.authenticated) {
@ -207,12 +202,12 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
}
}
async function showIfAuthenticated(selector, context = 'general') {
async function showIfAuthenticated(selector: string, context = 'general') {
const authStatus = await checkClientAuth(context);
const element = document.querySelector(selector);
if (element) {
element.style.display = (!authStatus.authRequired || authStatus.authenticated)
(element as HTMLElement).style.display = (!authStatus.authRequired || authStatus.authenticated)
? 'inline-flex'
: 'none';
}

31
src/utils/clientUtils.ts Normal file
View File

@ -0,0 +1,31 @@
// src/utils/clientUtils.js
// Client-side utilities that mirror server-side toolHelpers.ts
export function createToolSlug(toolName) {
if (!toolName || typeof toolName !== 'string') {
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
return '';
}
return toolName.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '') // Remove special characters
.replace(/\s+/g, '-') // Replace spaces with hyphens
.replace(/-+/g, '-') // Remove duplicate hyphens
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
}
export function findToolByIdentifier(tools, identifier) {
if (!identifier || !Array.isArray(tools)) return undefined;
return tools.find(tool =>
tool.name === identifier ||
createToolSlug(tool.name) === identifier.toLowerCase()
);
}
export function isToolHosted(tool) {
return tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
tool.projectUrl.trim() !== "";
}