Implement Map of Contents Architecture for Dynamic Tool Curation
Summary
Implemented a new Map of Contents (MOC) architecture that enables dynamic tool curation through markdown files, replacing hardcoded tool limits with configurable content-driven pipelines.
Why Care
This architecture provides a flexible, maintainable way to curate and display tools across multiple pages without code changes. Content editors can now manage tool lists through simple markdown files, while developers maintain a clean separation between content and presentation logic. The system supports both explicit tool references and tag-based filtering with configurable limits.
Implementation
Changes Made
New Content Collection Architecture
- Created
content/moc/directory for Map of Contents files - Added
mapOfContentsCollectiontosite/src/content.config.ts - Implemented MOC schema with support for
title,description,type, andMAX_CARDSfields - Added MOC path to content collection exports
Core Utility Functions
parseMocContent()insite/src/utils/toolUtils.ts- Parses markdown content to extract tool IDs and tag filtersloadToolsFromMoc()insite/src/utils/toolUtils.ts- Orchestrates tool loading using the same logic as AstroMarkdown directives- Enhanced
resolveToolId()- Maintains compatibility with existing tool resolution logic
MainContent Component Updates
- Modified
site/src/components/MainContent.astroto load tools from MOC instead of hardcoded limits - Replaced
getEntry()withgetCollection()for more robust entry discovery - Removed
TOOL_CARDGRID_LIMITconstant in favor of dynamic MOC-driven limits
Configuration Files
- Updated
content/moc/Home.mdwith frontmatter includingMAX_CARDS: 20 - Enhanced content collection schema to support optional
MAX_CARDSfield
Technical Details
MOC File Structure
markdown
---
title: "Home Page Tools"
description: "Tools listed on the Home Page (lossless.group)"
type: "tools"
MAX_CARDS: 20
---
- [[Assembly AI]]
- [[Flowise]]
- tag: [[AI Toolkit]] Content Collection Configuration
typescript
// site/src/content.config.ts
const mapOfContentsCollection = defineCollection({
loader: glob({ pattern: "**/*.md", base: resolveContentPath("moc") }),
schema: z.object({
title: z.string().optional(),
description: z.string().optional(),
type: z.string().optional(),
MAX_CARDS: z.number().optional(),
}).passthrough().transform((data, context) => {
const filename = String(context.path).split('/').pop()?.replace(/\.md$/, '') || '';
const displayTitle = data.title
? data.title
: filename.replace(/_/g, ' ').replace(/\s+/g, ' ').trim();
return {
...data,
title: displayTitle,
slug: filename.toLowerCase().replace(/\s+/g, '-'),
};
})
}); Tool Loading Pipeline
typescript
// site/src/components/MainContent.astro
const mocEntries = await getCollection("moc");
const homeMocEntry = mocEntries.find(entry => entry.id === "home");
const toolEntries = await getCollection("tooling");
const tools = homeMocEntry ? await loadToolsFromMoc(homeMocEntry, toolEntries) : []; Content Parsing Logic
The
parseMocContent() function handles multiple input formats:- Backlink format:
- [[Tool Name]]β Extracts "Tool Name" - Tag filtering:
- tag: [[Tag Name]]β Extracts "Tag Name" for filtering - Regular links:
- [Tool Name](path)β Extracts path for resolution
Tag Matching System
Uses the same normalization logic as AstroMarkdown:
typescript
const normalizeTag = (tag: string) => slugify(tag).toLowerCase();
// Matches tools with tags using case-insensitive slugified comparison Integration Points
AstroMarkdown Compatibility
- Replicates exact logic from
toolingGallerydirective inAstroMarkdown.astro - Uses same
resolveToolId()function for consistent tool resolution - Maintains tag filtering behavior identical to directive implementation
Content Collection Integration
- Leverages existing
toolingcollection for tool data - Uses
routeManager.tsfor path-to-route transformations - Integrates with
slugifyutilities for consistent string normalization
Frontmatter Configuration
- Supports optional
MAX_CARDSwith default value of 20 - Maintains backward compatibility with existing frontmatter patterns
- Uses Astro's content collection transforms for automatic slug generation
Documentation
For Content Editors
- Create MOC files in
content/moc/directory - Use frontmatter to configure title, description, type, and MAX_CARDS
- List tools using backlink format:
- [[Tool Name]] - Add tag filters using:
- tag: [[Tag Name]] - Set MAX_CARDS to limit total displayed tools
For Developers
- Import
loadToolsFromMocfrom@utils/toolUtils - Get MOC entry using
getCollection("moc") - Call
loadToolsFromMoc(mocEntry, allTools)to get curated tools - Pass tools array to existing card grid components
File Structure
text
content/
βββ moc/
β βββ Home.md # Homepage tool curation
β βββ About.md # About page tool curation
β βββ [page-name].md # Additional page curations
βββ tooling/ # Existing tool collection
site/src/
βββ content.config.ts # MOC collection definition
βββ utils/toolUtils.ts # MOC parsing and loading logic
βββ components/
βββ MainContent.astro # Updated to use MOC Example Usage Patterns
markdown
# Explicit tool selection
- [[Assembly AI]]
- [[Flowise]]
# Tag-based filtering
- tag: [[AI Toolkit]]
- tag: [[Machine Learning]]
# Mixed approach
- [[Specific Tool]]
- tag: [[Category]] Performance Considerations
- Lazy loading of tool data only when MOC entries are accessed
- Caching through Astro's content collection system
- Efficient parsing with regex-based content extraction
- Configurable limits prevent performance issues with large tool sets
Error Handling
- Graceful fallbacks when MOC entries are not found
- Warning logs for missing tools or tags
- Default behavior when MAX_CARDS is not specified
- Debug logging for troubleshooting parsing issues
This architecture provides a scalable, maintainable solution for dynamic content curation while maintaining full compatibility with existing Astro components and content collections.