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 mapOfContentsCollection to site/src/content.config.ts
  • Implemented MOC schema with support for title, description, type, and MAX_CARDS fields
  • Added MOC path to content collection exports

Core Utility Functions

  • parseMocContent() in site/src/utils/toolUtils.ts - Parses markdown content to extract tool IDs and tag filters
  • loadToolsFromMoc() in site/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.astro to load tools from MOC instead of hardcoded limits
  • Replaced getEntry() with getCollection() for more robust entry discovery
  • Removed TOOL_CARDGRID_LIMIT constant in favor of dynamic MOC-driven limits

Configuration Files

  • Updated content/moc/Home.md with frontmatter including MAX_CARDS: 20
  • Enhanced content collection schema to support optional MAX_CARDS field

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 toolingGallery directive in AstroMarkdown.astro
  • Uses same resolveToolId() function for consistent tool resolution
  • Maintains tag filtering behavior identical to directive implementation

Content Collection Integration

  • Leverages existing tooling collection for tool data
  • Uses routeManager.ts for path-to-route transformations
  • Integrates with slugify utilities for consistent string normalization

Frontmatter Configuration

  • Supports optional MAX_CARDS with 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

  1. Create MOC files in content/moc/ directory
  2. Use frontmatter to configure title, description, type, and MAX_CARDS
  3. List tools using backlink format: - [[Tool Name]]
  4. Add tag filters using: - tag: [[Tag Name]]
  5. Set MAX_CARDS to limit total displayed tools

For Developers

  1. Import loadToolsFromMoc from @utils/toolUtils
  2. Get MOC entry using getCollection("moc")
  3. Call loadToolsFromMoc(mocEntry, allTools) to get curated tools
  4. 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.