worker-1: completed task task-content-processing [failed]

This commit is contained in:
coordinator 2026-04-08 00:09:25 +00:00
parent 23a9dab8fd
commit 4460724fdb
8 changed files with 715 additions and 33 deletions

View File

@ -15,6 +15,7 @@ description: |
- Search implementation - Search implementation
- Content loading system - Content loading system
assigned_to: worker-1 assigned_to: worker-1
status: in_progress status: failed
result: 'qwen: agent loop: agent exceeded max steps (20)'
created_at: 2026-04-08T00:01:49.285216405Z created_at: 2026-04-08T00:01:49.285216405Z
updated_at: 2026-04-08T00:05:44.477892521Z updated_at: 2026-04-08T00:09:25.144326221Z

View File

@ -1,12 +1,21 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import './SearchBar.css'; import './SearchBar.css';
const SearchBar = ({ onSearch }) => { const SearchBar = ({ onSearch, categories }) => {
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [selectedCategory, setSelectedCategory] = useState('All');
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
onSearch(searchTerm); onSearch(searchTerm, selectedCategory);
};
const handleSearchChange = (e) => {
setSearchTerm(e.target.value);
};
const handleCategoryChange = (e) => {
setSelectedCategory(e.target.value);
}; };
return ( return (
@ -16,8 +25,14 @@ const SearchBar = ({ onSearch }) => {
type="text" type="text"
placeholder="Search jargon terms..." placeholder="Search jargon terms..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={handleSearchChange}
/> />
<select value={selectedCategory} onChange={handleCategoryChange}>
<option value="All">All Categories</option>
{categories && categories.map(category => (
<option key={category} value={category}>{category}</option>
))}
</select>
<button type="submit">Search</button> <button type="submit">Search</button>
</form> </form>
</div> </div>

172
src/models/JargonEntry.js Normal file
View File

@ -0,0 +1,172 @@
/**
* Data model for Jargon File entries
*/
export class JargonEntry {
constructor(data) {
this.id = data.id || Date.now() + Math.floor(Math.random() * 1000);
this.term = data.term || '';
this.definition = data.definition || '';
this.category = data.category || 'Uncategorized';
this.relatedTerms = data.relatedTerms || [];
this.dateAdded = data.dateAdded || new Date().toISOString().split('T')[0];
this.dateModified = data.dateModified || new Date().toISOString().split('T')[0];
}
/**
* Validates that the entry has required fields
* @returns {boolean} True if valid
*/
isValid() {
return this.term && this.definition;
}
/**
* Updates the entry with new data
* @param {Object} newData - New data to update
*/
update(newData) {
Object.assign(this, newData);
this.dateModified = new Date().toISOString().split('T')[0];
}
/**
* Converts the entry to a plain object
* @returns {Object} Plain object representation
*/
toObject() {
return {
id: this.id,
term: this.term,
definition: this.definition,
category: this.category,
relatedTerms: this.relatedTerms,
dateAdded: this.dateAdded,
dateModified: this.dateModified
};
}
/**
* Creates a formatted string representation
* @returns {string} Formatted string
*/
toString() {
return `${this.term}: ${this.definition.substring(0, 100)}...`;
}
}
export class JargonEntryCollection {
constructor(entries = []) {
this.entries = entries.map(entry => new JargonEntry(entry));
this.categories = this._buildCategories();
}
/**
* Builds categories from entries
* @private
*/
_buildCategories() {
const categories = {};
this.entries.forEach(entry => {
const category = entry.category;
if (!categories[category]) {
categories[category] = [];
}
categories[category].push(entry);
});
return categories;
}
/**
* Adds a new entry
* @param {Object} data - Entry data
* @returns {JargonEntry} The created entry
*/
addEntry(data) {
const entry = new JargonEntry(data);
if (entry.isValid()) {
this.entries.push(entry);
this.categories = this._buildCategories(); // Rebuild categories
return entry;
}
throw new Error('Invalid entry data');
}
/**
* Gets an entry by ID
* @param {number} id - Entry ID
* @returns {JargonEntry} The entry or undefined
*/
getEntryById(id) {
return this.entries.find(entry => entry.id === id);
}
/**
* Gets an entry by term
* @param {string} term - Term to search for
* @returns {JargonEntry} The entry or undefined
*/
getEntryByTerm(term) {
return this.entries.find(entry =>
entry.term.toLowerCase() === term.toLowerCase()
);
}
/**
* Gets entries by category
* @param {string} category - Category name
* @returns {Array} Array of entries in the category
*/
getEntriesByCategory(category) {
return this.entries.filter(entry =>
entry.category.toLowerCase() === category.toLowerCase()
);
}
/**
* Gets all categories
* @returns {Array} Array of category names
*/
getCategories() {
return Object.keys(this.categories);
}
/**
* Gets all entries
* @returns {Array} Array of all entries
*/
getAllEntries() {
return this.entries;
}
/**
* Removes an entry
* @param {number} id - Entry ID to remove
* @returns {boolean} True if removed
*/
removeEntry(id) {
const index = this.entries.findIndex(entry => entry.id === id);
if (index !== -1) {
this.entries.splice(index, 1);
this.categories = this._buildCategories(); // Rebuild categories
return true;
}
return false;
}
/**
* Updates an entry
* @param {number} id - Entry ID to update
* @param {Object} newData - New data
* @returns {JargonEntry} Updated entry or undefined
*/
updateEntry(id, newData) {
const entry = this.getEntryById(id);
if (entry) {
entry.update(newData);
this.categories = this._buildCategories(); // Rebuild categories
return entry;
}
return undefined;
}
}

View File

@ -1,38 +1,60 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import EntryList from '../components/EntryList'; import EntryList from '../components/EntryList';
import SearchBar from '../components/SearchBar'; import SearchBar from '../components/SearchBar';
import { contentLoader } from '../services/contentLoader';
import { searchEntries } from '../utils/searchUtils';
import './Home.css'; import './Home.css';
const Home = () => { const Home = () => {
const [entries, setEntries] = useState([]); const [entries, setEntries] = useState([]);
const [filteredEntries, setFilteredEntries] = useState([]); const [filteredEntries, setFilteredEntries] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => { useEffect(() => {
// In a real app, this would fetch from an API or data file // Load entries from content loader
// For now, we'll use mock data const loadEntries = async () => {
const mockEntries = [ try {
{ // Load the existing data from jargonEntries.json
id: 1, const response = await fetch('/src/data/jargonEntries.json');
term: "Hacker", const data = await response.json();
definition: "A person who enjoys exploring the details of programmable systems and how to stretch their capabilities.",
category: "Core Terms"
},
{
id: 2,
term: "Guru",
definition: "A person who has achieved a high level of skill in a particular field or domain.",
category: "Core Terms"
},
{
id: 3,
term: "Foo",
definition: "A generic term used as a placeholder in examples and documentation.",
category: "Technical Terms"
}
];
setEntries(mockEntries); // Store in content loader for future use
setFilteredEntries(mockEntries); await contentLoader.loadContent(JSON.stringify(data), 'json');
setEntries(data);
setFilteredEntries(data);
setIsLoading(false);
} catch (error) {
console.error('Error loading entries:', error);
// Fallback to mock data
const mockEntries = [
{
id: 1,
term: "Hacker",
definition: "A person who enjoys exploring the details of programmable systems and how to stretch their capabilities.",
category: "Core Terms"
},
{
id: 2,
term: "Guru",
definition: "A person who has achieved a high level of skill in a particular field or domain.",
category: "Core Terms"
},
{
id: 3,
term: "Foo",
definition: "A generic term used as a placeholder in examples and documentation.",
category: "Technical Terms"
}
];
setEntries(mockEntries);
setFilteredEntries(mockEntries);
setIsLoading(false);
}
};
loadEntries();
}, []); }, []);
const handleSearch = (searchTerm) => { const handleSearch = (searchTerm) => {
@ -41,14 +63,22 @@ const Home = () => {
return; return;
} }
const filtered = entries.filter(entry => const filtered = searchEntries(entries, searchTerm);
entry.term.toLowerCase().includes(searchTerm.toLowerCase()) ||
entry.definition.toLowerCase().includes(searchTerm.toLowerCase())
);
setFilteredEntries(filtered); setFilteredEntries(filtered);
}; };
if (isLoading) {
return (
<div className="home">
<div className="hero">
<h2>Explore Hacker Culture</h2>
<p>Discover the terminology that defines computer hacker culture</p>
</div>
<p>Loading jargon entries...</p>
</div>
);
}
return ( return (
<div className="home"> <div className="home">
<div className="hero"> <div className="hero">

View File

@ -0,0 +1,57 @@
import React, { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import EntryList from '../components/EntryList';
import SearchBar from '../components/SearchBar';
import { contentLoader } from '../services/contentLoader';
import { searchEntries } from '../utils/searchUtils';
import './SearchResults.css';
const SearchResults = () => {
const [entries, setEntries] = useState([]);
const [filteredEntries, setFilteredEntries] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const location = useLocation();
useEffect(() => {
// Load entries from content loader
const loadEntries = async () => {
// In a real app, we'd load from actual data source
// For now, using the existing data in the data file
const entriesData = contentLoader.getAllEntries();
setEntries(entriesData);
setFilteredEntries(entriesData);
};
loadEntries();
}, []);
useEffect(() => {
// Handle search from URL parameters
const urlParams = new URLSearchParams(location.search);
const search = urlParams.get('q');
if (search) {
setSearchTerm(search);
const results = searchEntries(entries, search);
setFilteredEntries(results);
}
}, [location.search, entries]);
const handleSearch = (searchTerm) => {
setSearchTerm(searchTerm);
const results = searchEntries(entries, searchTerm);
setFilteredEntries(results);
};
return (
<div className="search-results">
<div className="hero">
<h2>Search Results</h2>
<p>Find jargon terms and definitions</p>
</div>
<SearchBar onSearch={handleSearch} />
<EntryList entries={filteredEntries} />
</div>
);
};
export default SearchResults;

View File

@ -0,0 +1,150 @@
/**
* Content Loading System for Jargon File
* Handles loading, parsing, and managing jargon entries
*/
import { parseContent, structureData } from '../utils/contentParser';
import { JargonEntryCollection } from '../models/JargonEntry';
/**
* Content Loader Class
*/
export class ContentLoader {
constructor() {
this.entries = [];
this.collection = null;
this.isLoaded = false;
}
/**
* Load content from a source
* @param {string} source - Source content
* @param {string} format - Format of the source ('json', 'text', 'html')
* @returns {Promise<boolean>} True if successful
*/
async loadContent(source, format = 'json') {
try {
const parsedEntries = parseContent(source, format);
this.entries = parsedEntries;
this.collection = new JargonEntryCollection(parsedEntries);
this.isLoaded = true;
return true;
} catch (error) {
console.error('Error loading content:', error);
return false;
}
}
/**
* Load content from a file (browser environment)
* @param {File} file - File to load
* @returns {Promise<boolean>} True if successful
*/
async loadFromFile(file) {
try {
const reader = new FileReader();
const content = await new Promise((resolve, reject) => {
reader.onload = (e) => resolve(e.target.result);
reader.onerror = (e) => reject(e);
reader.readAsText(file);
});
const format = this._detectFormat(file.name);
return await this.loadContent(content, format);
} catch (error) {
console.error('Error loading file:', error);
return false;
}
}
/**
* Load content from URL
* @param {string} url - URL to fetch content from
* @returns {Promise<boolean>} True if successful
*/
async loadFromURL(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const content = await response.text();
const format = this._detectFormat(url);
return await this.loadContent(content, format);
} catch (error) {
console.error('Error loading from URL:', error);
return false;
}
}
/**
* Detect format from filename or URL
* @private
* @param {string} source - Source name or URL
* @returns {string} Detected format
*/
_detectFormat(source) {
if (source.endsWith('.json')) {
return 'json';
} else if (source.endsWith('.txt')) {
return 'text';
} else if (source.endsWith('.html') || source.endsWith('.htm')) {
return 'html';
}
return 'json'; // Default
}
/**
* Get all loaded entries
* @returns {Array} Array of entries
*/
getEntries() {
return this.entries;
}
/**
* Get the entry collection
* @returns {JargonEntryCollection} The collection
*/
getCollection() {
return this.collection;
}
/**
* Check if content is loaded
* @returns {boolean} True if loaded
*/
isContentLoaded() {
return this.isLoaded;
}
/**
* Get categories
* @returns {Array} Array of categories
*/
getCategories() {
return this.collection ? this.collection.getCategories() : [];
}
/**
* Get entries by category
* @param {string} category - Category name
* @returns {Array} Array of entries
*/
getEntriesByCategory(category) {
return this.collection ? this.collection.getEntriesByCategory(category) : [];
}
/**
* Get all entries (alias for getEntries)
* @returns {Array} Array of all entries
*/
getAllEntries() {
return this.collection ? this.collection.getAllEntries() : [];
}
}
/**
* Singleton instance of ContentLoader
*/
export const contentLoader = new ContentLoader();

149
src/utils/contentParser.js Normal file
View File

@ -0,0 +1,149 @@
/**
* Content Parser for Jargon File entries
* This utility handles parsing content from various formats into structured data
*/
/**
* Parse JSON format jargon entries
* @param {string} content - JSON string containing jargon entries
* @returns {Array} Array of parsed jargon entries
*/
export const parseJSON = (content) => {
try {
const data = JSON.parse(content);
return data.map(entry => ({
id: entry.id,
term: entry.term,
definition: entry.definition,
category: entry.category || 'Uncategorized',
relatedTerms: entry.relatedTerms || [],
dateAdded: entry.dateAdded || new Date().toISOString().split('T')[0]
}));
} catch (error) {
console.error('Error parsing JSON:', error);
return [];
}
};
/**
* Parse text format jargon entries (simple format)
* @param {string} content - Text content with entries
* @returns {Array} Array of parsed jargon entries
*/
export const parseText = (content) => {
// Simple text parsing logic
const entries = [];
const lines = content.split('\n');
let currentEntry = null;
lines.forEach(line => {
if (line.trim().startsWith('TERM:')) {
if (currentEntry) {
entries.push(currentEntry);
}
currentEntry = {
term: line.replace('TERM:', '').trim(),
definition: '',
category: 'Uncategorized',
relatedTerms: [],
dateAdded: new Date().toISOString().split('T')[0]
};
} else if (line.trim().startsWith('DEFINITION:')) {
if (currentEntry) {
currentEntry.definition = line.replace('DEFINITION:', '').trim();
}
} else if (line.trim().startsWith('CATEGORY:')) {
if (currentEntry) {
currentEntry.category = line.replace('CATEGORY:', '').trim();
}
} else if (line.trim().startsWith('RELATED:')) {
if (currentEntry) {
currentEntry.relatedTerms = line.replace('RELATED:', '').trim().split(',').map(term => term.trim());
}
}
});
if (currentEntry) {
entries.push(currentEntry);
}
return entries;
};
/**
* Parse HTML format jargon entries
* @param {string} content - HTML content containing jargon entries
* @returns {Array} Array of parsed jargon entries
*/
export const parseHTML = (content) => {
// For now, we'll provide a basic structure - in practice this would parse HTML
// This is a simplified version that would need to be expanded
const entries = [];
// This is a placeholder - would need more sophisticated HTML parsing
// For the purpose of this implementation, we'll just return empty entries
// In a real implementation, we would parse HTML tags like <term>, <definition>, etc.
return entries;
};
/**
* Generic parse function that tries to determine the format and parse accordingly
* @param {string} content - Content to parse
* @param {string} format - Format to parse as ('json', 'text', 'html')
* @returns {Array} Array of parsed jargon entries
*/
export const parseContent = (content, format) => {
switch (format.toLowerCase()) {
case 'json':
return parseJSON(content);
case 'text':
return parseText(content);
case 'html':
return parseHTML(content);
default:
// Try to auto-detect format
try {
JSON.parse(content);
return parseJSON(content);
} catch {
return parseText(content);
}
}
};
/**
* Structure data for efficient storage and retrieval
* @param {Array} entries - Array of jargon entries
* @returns {Object} Structured data with categories and indexes
*/
export const structureData = (entries) => {
const categories = {};
const termIndex = {};
const categoryIndex = {};
entries.forEach(entry => {
// Build category mapping
const category = entry.category;
if (!categories[category]) {
categories[category] = [];
}
categories[category].push(entry.id);
// Build term index for fast lookup
termIndex[entry.term.toLowerCase()] = entry.id;
// Build category index
if (!categoryIndex[category]) {
categoryIndex[category] = [];
}
categoryIndex[category].push(entry.id);
});
return {
entries,
categories,
termIndex,
categoryIndex
};
};

108
src/utils/searchUtils.js Normal file
View File

@ -0,0 +1,108 @@
/**
* Search Utilities for Jargon File
* Handles searching through jargon entries
*/
/**
* Search through jargon entries
* @param {Array} entries - Array of jargon entries
* @param {string} searchTerm - Search term
* @returns {Array} Filtered entries
*/
export const searchEntries = (entries, searchTerm) => {
if (!searchTerm || searchTerm.trim() === '') {
return entries;
}
const term = searchTerm.toLowerCase().trim();
return entries.filter(entry => {
// Search in term, definition, and related terms
const termMatch = entry.term.toLowerCase().includes(term);
const definitionMatch = entry.definition.toLowerCase().includes(term);
const relatedTermsMatch = entry.relatedTerms.some(term =>
term.toLowerCase().includes(term)
);
return termMatch || definitionMatch || relatedTermsMatch;
});
};
/**
* Advanced search with category filtering
* @param {Array} entries - Array of jargon entries
* @param {string} searchTerm - Search term
* @param {string} category - Category to filter by (optional)
* @returns {Array} Filtered entries
*/
export const advancedSearch = (entries, searchTerm, category = null) => {
let results = entries;
// Apply category filter if specified
if (category && category !== 'All') {
results = results.filter(entry =>
entry.category.toLowerCase() === category.toLowerCase()
);
}
// Apply search term filter
if (searchTerm && searchTerm.trim() !== '') {
results = searchEntries(results, searchTerm);
}
return results;
};
/**
* Get search suggestions based on current entries
* @param {Array} entries - Array of jargon entries
* @param {string} searchTerm - Current search term
* @returns {Array} Suggested terms
*/
export const getSearchSuggestions = (entries, searchTerm) => {
if (!searchTerm || searchTerm.trim() === '') {
return [];
}
const term = searchTerm.toLowerCase().trim();
const suggestions = new Set();
entries.forEach(entry => {
// Add exact matches
if (entry.term.toLowerCase().startsWith(term)) {
suggestions.add(entry.term);
}
// Add partial matches in definition
if (entry.definition.toLowerCase().includes(term)) {
// Extract potential terms from definition for suggestions
// This is a simple implementation - more sophisticated would use NLP
const words = entry.definition.toLowerCase().split(/\s+/);
words.forEach(word => {
if (word.startsWith(term) && word.length > 2) {
suggestions.add(word);
}
});
}
});
return Array.from(suggestions).slice(0, 5); // Return top 5 suggestions
};
/**
* Get search statistics
* @param {Array} entries - Array of jargon entries
* @param {string} searchTerm - Search term
* @returns {Object} Search statistics
*/
export const getSearchStats = (entries, searchTerm) => {
const totalEntries = entries.length;
const filteredEntries = searchEntries(entries, searchTerm);
return {
totalEntries,
filteredEntries: filteredEntries.length,
searchTerm,
searchRatio: totalEntries > 0 ? (filteredEntries.length / totalEntries * 100).toFixed(1) : 0
};
};