Spaces:
Running
Running
| // Color schemes for different visualization types | |
| export const VISUALIZATION_COLORS = { | |
| // Entity type colors | |
| entityTypes: { | |
| person: "#3b82f6", | |
| organization: "#10b981", | |
| location: "#f59e0b", | |
| concept: "#8b5cf6", | |
| event: "#ef4444", | |
| default: "#6b7280", | |
| }, | |
| // Processing method colors | |
| processingMethods: { | |
| production: "#059669", | |
| baseline: "#dc2626", | |
| hybrid: "#7c3aed", | |
| sequential: "#ea580c", | |
| default: "#6b7280", | |
| }, | |
| // Chart color palettes | |
| sequential: [ | |
| "#f7fafc", | |
| "#edf2f7", | |
| "#e2e8f0", | |
| "#cbd5e0", | |
| "#a0aec0", | |
| "#718096", | |
| "#4a5568", | |
| "#2d3748", | |
| "#1a202c", | |
| ], | |
| categorical: [ | |
| "#3b82f6", | |
| "#10b981", | |
| "#f59e0b", | |
| "#8b5cf6", | |
| "#ef4444", | |
| "#06b6d4", | |
| "#84cc16", | |
| "#f97316", | |
| "#ec4899", | |
| "#6366f1", | |
| ], | |
| diverging: [ | |
| "#dc2626", | |
| "#ef4444", | |
| "#f87171", | |
| "#fca5a5", | |
| "#fed7d7", | |
| "#f3f4f6", | |
| "#d1fae5", | |
| "#86efac", | |
| "#34d399", | |
| "#10b981", | |
| ], | |
| }; | |
| // Text processing utilities | |
| export function removeStopwords( | |
| text: string, | |
| customStopwords: string[] = [] | |
| ): string { | |
| const defaultStopwords = [ | |
| "the", | |
| "be", | |
| "to", | |
| "of", | |
| "and", | |
| "a", | |
| "in", | |
| "that", | |
| "have", | |
| "i", | |
| "it", | |
| "for", | |
| "not", | |
| "on", | |
| "with", | |
| "he", | |
| "as", | |
| "you", | |
| "do", | |
| "at", | |
| "this", | |
| "but", | |
| "his", | |
| "by", | |
| "from", | |
| "they", | |
| "we", | |
| "say", | |
| "her", | |
| "she", | |
| "or", | |
| "an", | |
| "will", | |
| "my", | |
| "one", | |
| "all", | |
| "would", | |
| "there", | |
| "their", | |
| "what", | |
| "so", | |
| "up", | |
| "out", | |
| "if", | |
| "about", | |
| "who", | |
| "get", | |
| "which", | |
| "go", | |
| "me", | |
| "when", | |
| "make", | |
| "can", | |
| "like", | |
| "time", | |
| "no", | |
| "just", | |
| "him", | |
| "know", | |
| "take", | |
| "people", | |
| "into", | |
| "year", | |
| "your", | |
| "good", | |
| "some", | |
| "could", | |
| "them", | |
| "see", | |
| "other", | |
| "than", | |
| "then", | |
| "now", | |
| "look", | |
| "only", | |
| "come", | |
| "its", | |
| "over", | |
| "think", | |
| "also", | |
| "back", | |
| "after", | |
| "use", | |
| "two", | |
| "how", | |
| "our", | |
| "work", | |
| "first", | |
| "well", | |
| "way", | |
| "even", | |
| "new", | |
| "want", | |
| "because", | |
| "any", | |
| "these", | |
| "give", | |
| "day", | |
| "most", | |
| "us", | |
| "is", | |
| "was", | |
| "are", | |
| "been", | |
| "has", | |
| "had", | |
| "were", | |
| "said", | |
| "each", | |
| ]; | |
| const allStopwords = new Set([...defaultStopwords, ...customStopwords]); | |
| return text | |
| .toLowerCase() | |
| .replace(/[^\w\s]/g, " ") | |
| .split(/\s+/) | |
| .filter((word) => word.length > 2 && !allStopwords.has(word)) | |
| .join(" "); | |
| } | |
| export function extractKeywords( | |
| text: string, | |
| maxKeywords: number = 20 | |
| ): string[] { | |
| const processedText = removeStopwords(text); | |
| const words = processedText.split(/\s+/); | |
| // Calculate word frequency | |
| const frequency: Record<string, number> = {}; | |
| words.forEach((word) => { | |
| frequency[word] = (frequency[word] || 0) + 1; | |
| }); | |
| // Return top keywords by frequency | |
| return Object.entries(frequency) | |
| .sort(([, a], [, b]) => b - a) | |
| .slice(0, maxKeywords) | |
| .map(([word]) => word); | |
| } | |
| export function calculateTfIdf( | |
| documents: string[], | |
| maxTerms: number = 100 | |
| ): Array<{ word: string; score: number }> { | |
| // Calculate TF-IDF scores for term extraction | |
| const documentWords = documents.map((doc) => | |
| removeStopwords(doc).split(/\s+/) | |
| ); | |
| const vocabulary = new Set(documentWords.flat()); | |
| const tfidfScores: Record<string, number> = {}; | |
| vocabulary.forEach((term) => { | |
| let tfidfSum = 0; | |
| documentWords.forEach((docWords) => { | |
| const tf = | |
| docWords.filter((word) => word === term).length / docWords.length; | |
| const df = documentWords.filter((doc) => doc.includes(term)).length; | |
| const idf = Math.log(documents.length / df); | |
| tfidfSum += tf * idf; | |
| }); | |
| tfidfScores[term] = tfidfSum; | |
| }); | |
| return Object.entries(tfidfScores) | |
| .sort(([, a], [, b]) => b - a) | |
| .slice(0, maxTerms) | |
| .map(([word, score]) => ({ word, score })); | |
| } | |
| // Color utility functions | |
| export function getColorForType( | |
| type: string, | |
| colorScheme: "entity" | "processing" = "entity" | |
| ): string { | |
| const schemes = { | |
| entity: VISUALIZATION_COLORS.entityTypes, | |
| processing: VISUALIZATION_COLORS.processingMethods, | |
| }; | |
| return ( | |
| schemes[colorScheme][type as keyof (typeof schemes)[typeof colorScheme]] || | |
| schemes[colorScheme].default | |
| ); | |
| } | |
| export function getColorScale( | |
| count: number, | |
| type: "sequential" | "categorical" | "diverging" = "categorical" | |
| ): string[] { | |
| const palette = VISUALIZATION_COLORS[type]; | |
| if (count <= palette.length) { | |
| return palette.slice(0, count); | |
| } | |
| // Generate interpolated colors for larger counts | |
| const step = Math.floor(palette.length / count); | |
| const colors: string[] = []; | |
| for (let i = 0; i < count; i++) { | |
| const index = Math.min(i * step, palette.length - 1); | |
| colors.push(palette[index] || "#6b7280"); | |
| } | |
| return colors; | |
| } | |
| export function hexToRgba(hex: string, alpha: number = 1): string { | |
| const r = parseInt(hex.slice(1, 3), 16); | |
| const g = parseInt(hex.slice(3, 5), 16); | |
| const b = parseInt(hex.slice(5, 7), 16); | |
| return `rgba(${r}, ${g}, ${b}, ${alpha})`; | |
| } | |
| // Text analysis utilities | |
| export function analyzeTextReadability(text: string): { | |
| readingTime: number; | |
| complexity: number; | |
| wordCount: number; | |
| sentenceCount: number; | |
| avgWordsPerSentence: number; | |
| avgSyllablesPerWord: number; | |
| } { | |
| const words = text.split(/\s+/).filter((word) => word.length > 0); | |
| const sentences = text.split(/[.!?]+/).filter((s) => s.trim().length > 0); | |
| const wordCount = words.length; | |
| const sentenceCount = sentences.length; | |
| const avgWordsPerSentence = wordCount / sentenceCount; | |
| // Simple syllable counting (approximation) | |
| const avgSyllablesPerWord = | |
| words.reduce((sum, word) => { | |
| const syllables = word | |
| .toLowerCase() | |
| .replace(/[^a-z]/g, "") | |
| .replace(/[aeiou]{2,}/g, "a") | |
| .match(/[aeiou]/g); | |
| return sum + (syllables ? syllables.length : 1); | |
| }, 0) / wordCount; | |
| // Flesch-Kincaid Grade Level (simplified) | |
| const complexity = Math.max( | |
| 0, | |
| Math.min( | |
| 100, | |
| 206.835 - 1.015 * avgWordsPerSentence - 84.6 * avgSyllablesPerWord | |
| ) | |
| ); | |
| // Reading time (200 words per minute) | |
| const readingTime = Math.ceil(wordCount / 200); | |
| return { | |
| readingTime, | |
| complexity, | |
| wordCount, | |
| sentenceCount, | |
| avgWordsPerSentence, | |
| avgSyllablesPerWord, | |
| }; | |
| } | |
| // Data transformation utilities | |
| export function normalizeValue( | |
| value: number, | |
| min: number, | |
| max: number | |
| ): number { | |
| return (value - min) / (max - min); | |
| } | |
| export function scaleValue( | |
| value: number, | |
| oldMin: number, | |
| oldMax: number, | |
| newMin: number, | |
| newMax: number | |
| ): number { | |
| return newMin + ((value - oldMin) * (newMax - newMin)) / (oldMax - oldMin); | |
| } | |
| export function formatNumber(num: number, decimals: number = 0): string { | |
| if (num >= 1000000) { | |
| return (num / 1000000).toFixed(decimals) + "M"; | |
| } else if (num >= 1000) { | |
| return (num / 1000).toFixed(decimals) + "K"; | |
| } else { | |
| return num.toFixed(decimals); | |
| } | |
| } | |
| export function formatPercentage(value: number, total: number): string { | |
| return `${((value / total) * 100).toFixed(1)}%`; | |
| } | |
| // Debounce utility for performance | |
| export function debounce<T extends (...args: any[]) => void>( | |
| func: T, | |
| delay: number | |
| ): (...args: Parameters<T>) => void { | |
| let timeoutId: NodeJS.Timeout; | |
| return (...args: Parameters<T>) => { | |
| clearTimeout(timeoutId); | |
| timeoutId = setTimeout(() => func(...args), delay); | |
| }; | |
| } | |
| // DOM utilities for visualization | |
| export function getElementDimensions(element: HTMLElement | null): { | |
| width: number; | |
| height: number; | |
| } { | |
| if (!element) return { width: 0, height: 0 }; | |
| const rect = element.getBoundingClientRect(); | |
| return { | |
| width: rect.width, | |
| height: rect.height, | |
| }; | |
| } | |
| export function createSvgElement(width: number, height: number): SVGSVGElement { | |
| const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); | |
| svg.setAttribute("width", width.toString()); | |
| svg.setAttribute("height", height.toString()); | |
| svg.setAttribute("viewBox", `0 0 ${width} ${height}`); | |
| return svg; | |
| } | |
| // Accessibility utilities | |
| export function generateAccessibleLabel( | |
| chartType: string, | |
| dataPoints: number, | |
| summary?: string | |
| ): string { | |
| const baseLabel = `${chartType} with ${dataPoints} data points`; | |
| return summary ? `${baseLabel}. ${summary}` : baseLabel; | |
| } | |
| export function getContrastColor(backgroundColor: string): string { | |
| // Simple contrast color calculation | |
| const hex = backgroundColor.replace("#", ""); | |
| const r = parseInt(hex.substr(0, 2), 16); | |
| const g = parseInt(hex.substr(2, 2), 16); | |
| const b = parseInt(hex.substr(4, 2), 16); | |
| const brightness = (r * 299 + g * 587 + b * 114) / 1000; | |
| return brightness > 128 ? "#000000" : "#ffffff"; | |
| } | |