Pq234/robot-learning-tutorial
0
1/**2 * LaTeX Metadata Extractor3 * Extracts document metadata from LaTeX files for frontmatter generation4 */5 6/**7 * Extract metadata from LaTeX content8 * @param {string} latexContent - Raw LaTeX content9 * @returns {object} - Extracted metadata object10 */11export function extractLatexMetadata(latexContent) {12 const metadata = {};13 14 // Extract title15 const titleMatch = latexContent.match(/\\title\s*\{\s*([^}]+)\s*\}/s);16 if (titleMatch) {17 metadata.title = titleMatch[1]18 .replace(/\\[a-zA-Z]+/g, '') // Remove LaTeX commands19 .replace(/\n/g, ' ')20 .trim();21 }22 23 // Extract authors with their specific affiliations24 const authors = [];25 const authorMatches = latexContent.matchAll(/\\authorOne\[[^\]]*\]\{([^}]+)\}/g);26 27 for (const match of authorMatches) {28 const fullAuthorInfo = match[1];29 30 // Determine affiliations based on macros present31 const affiliations = [];32 if (fullAuthorInfo.includes('\\ensps')) {33 affiliations.push(1); // École Normale Supérieure34 }35 if (fullAuthorInfo.includes('\\oxford')) {36 affiliations.push(1); // University of Oxford (index 1 dans le frontmatter)37 }38 if (fullAuthorInfo.includes('\\hf')) {39 affiliations.push(2); // Hugging Face (index 2 dans le frontmatter)40 }41 42 // Clean author name by removing macros43 let authorName = fullAuthorInfo44 .replace(/\\ensps/g, '') // Remove École macro45 .replace(/\\hf/g, '') // Remove Hugging Face macro46 .replace(/\\oxford/g, '') // Remove Oxford macro47 .replace(/\\[a-zA-Z]+/g, '') // Remove any other LaTeX commands48 .replace(/\s+/g, ' ') // Normalize whitespace49 .trim();50 51 // Skip empty authors or placeholder entries52 if (authorName && authorName !== '...') {53 authors.push({54 name: authorName,55 affiliations: affiliations.length > 0 ? affiliations : [2] // Default to HF if no macro56 });57 }58 }59 60 if (authors.length > 0) {61 metadata.authors = authors;62 }63 64 // Extract affiliations dynamically from \contribution command65 const contributionMatch = latexContent.match(/\\contribution\[\]\{([^}]+)\}/);66 if (contributionMatch) {67 const contributionText = contributionMatch[1];68 69 // Parse affiliations from contribution text70 const affiliations = [];71 72 // Split by common separators and clean up73 const parts = contributionText74 .split(/[,;]/)75 .map(part => part.trim())76 .filter(part => part.length > 0);77 78 for (const part of parts) {79 // Remove LaTeX commands and clean up80 const cleanName = part81 .replace(/\\[a-zA-Z]+/g, '') // Remove LaTeX commands like \oxford, \hf82 .replace(/\s+/g, ' ') // Normalize whitespace83 .trim();84 85 if (cleanName && cleanName.length > 0) {86 affiliations.push({87 name: cleanName88 });89 }90 }91 92 if (affiliations.length > 0) {93 metadata.affiliations = affiliations;94 }95 }96 97 // Fallback to hardcoded affiliations if no \contribution found98 if (!metadata.affiliations || metadata.affiliations.length === 0) {99 metadata.affiliations = [100 {101 name: "École Normale Supérieure Paris-Saclay"102 },103 {104 name: "University of Oxford"105 },106 {107 name: "Hugging Face"108 }109 ];110 }111 112 // Extract date if available (common LaTeX patterns)113 const datePatterns = [114 /\\date\s*\{([^}]+)\}/,115 /\\newcommand\s*\{\\date\}\s*\{([^}]+)\}/,116 ];117 118 for (const pattern of datePatterns) {119 const dateMatch = latexContent.match(pattern);120 if (dateMatch) {121 metadata.published = dateMatch[1].trim();122 break;123 }124 }125 126 // Fallback to current date if no date found127 if (!metadata.published) {128 metadata.published = new Date().toLocaleDateString('en-US', {129 year: 'numeric',130 month: 'short',131 day: '2-digit'132 });133 }134 135 return metadata;136}137 138/**139 * Generate YAML frontmatter from metadata object140 * @param {object} metadata - Metadata object141 * @returns {string} - YAML frontmatter string142 */143export function generateFrontmatter(metadata) {144 let frontmatter = '---\n';145 146 // Title147 if (metadata.title) {148 frontmatter += `title: "${metadata.title}"\n`;149 }150 151 // Authors152 if (metadata.authors && metadata.authors.length > 0) {153 frontmatter += 'authors:\n';154 metadata.authors.forEach(author => {155 frontmatter += ` - name: "${author.name}"\n`;156 if (author.url) {157 frontmatter += ` url: "${author.url}"\n`;158 }159 frontmatter += ` affiliations: [${author.affiliations.join(', ')}]\n`;160 });161 }162 163 // Affiliations164 if (metadata.affiliations && metadata.affiliations.length > 0) {165 frontmatter += 'affiliations:\n';166 metadata.affiliations.forEach((affiliation, index) => {167 frontmatter += ` - name: "${affiliation.name}"\n`;168 if (affiliation.url) {169 frontmatter += ` url: "${affiliation.url}"\n`;170 }171 });172 }173 174 // Publication date175 if (metadata.published) {176 frontmatter += `published: "${metadata.published}"\n`;177 }178 179 // Additional metadata180 if (metadata.doi) {181 frontmatter += `doi: "${metadata.doi}"\n`;182 }183 184 if (metadata.description) {185 frontmatter += `description: "${metadata.description}"\n`;186 }187 188 if (metadata.licence) {189 frontmatter += `licence: >\n ${metadata.licence}\n`;190 }191 192 if (metadata.tags && metadata.tags.length > 0) {193 frontmatter += 'tags:\n';194 metadata.tags.forEach(tag => {195 frontmatter += ` - ${tag}\n`;196 });197 }198 199 // Default Astro configuration200 frontmatter += 'tableOfContentsAutoCollapse: true\n';201 frontmatter += '---\n\n';202 203 return frontmatter;204}205 206/**207 * Extract and generate frontmatter from LaTeX content208 * @param {string} latexContent - Raw LaTeX content209 * @returns {string} - Complete YAML frontmatter210 */211export function extractAndGenerateFrontmatter(latexContent) {212 const metadata = extractLatexMetadata(latexContent);213 return generateFrontmatter(metadata);214}215 