Skip to content

Commit efa7152

Browse files
ofriwclaude
andcommitted
Preserve code blocks during validation, apply line-breaking after
- Defer line-breaking until after validation passes in generate-presentation.js - Add preserveCode parameter to parseMarkdownContent() in markdown-parser.js - Code blocks remain intact for validation, preventing false positives - Line-breaking applied only to validated presentations before deployment 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent a079bb9 commit efa7152

File tree

2 files changed

+62
-54
lines changed

2 files changed

+62
-54
lines changed

scripts/generate-presentation.js

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -722,21 +722,11 @@ async function generatePresentationWithClaude(prompt, outputPath) {
722722

723723
console.log(` ✅ Valid presentation JSON (${presentation.slides.length} slides)`);
724724

725-
// Apply deterministic line breaking
726-
console.log(' 🔧 Applying line breaking...');
727-
const { presentation: processedPresentation, stats } = processPresentation(presentation);
728-
729-
// Log statistics
730-
if (stats.linesShortened > 0) {
731-
console.log(` ✂️ Fixed ${stats.linesShortened} long lines (max reduction: ${stats.maxReduction} chars)`);
732-
} else {
733-
console.log(' ✅ No lines exceeded 60 characters');
734-
}
735-
736-
// Write back the processed presentation
737-
writeFileSync(outputPath, JSON.stringify(processedPresentation, null, 2), 'utf-8');
725+
// Write the unmodified presentation to file for validation
726+
// Line breaking will happen after validation passes
727+
writeFileSync(outputPath, JSON.stringify(presentation, null, 2), 'utf-8');
738728

739-
resolve(processedPresentation);
729+
resolve(presentation);
740730
} catch (parseError) {
741731
reject(new Error(`Failed to parse JSON: ${parseError.message}\nContent preview: ${fileContent?.slice(0, 200)}`));
742732
return;
@@ -1181,8 +1171,8 @@ async function generatePresentation(filePath, manifest, config) {
11811171
console.log(`\n📄 Generating presentation: ${relativePath}`);
11821172

11831173
try {
1184-
// Parse content
1185-
const content = parseMarkdownContent(filePath);
1174+
// Parse content (preserve code blocks for visual presentation)
1175+
const content = parseMarkdownContent(filePath, true);
11861176

11871177
if (content.length < 100) {
11881178
console.log(` ⚠️ Skipping - content too short`);
@@ -1297,24 +1287,38 @@ async function generatePresentation(filePath, manifest, config) {
12971287
console.log(` ✅ All ${codeSourceValidation.codeSlidesChecked} code slide(s) verified against source`);
12981288
}
12991289

1290+
// Apply deterministic line breaking (AFTER validation passes)
1291+
console.log(' 🔧 Applying line breaking...');
1292+
const { presentation: processedPresentation, stats } = processPresentation(presentation);
1293+
1294+
// Log statistics
1295+
if (stats.linesShortened > 0) {
1296+
console.log(` ✂️ Fixed ${stats.linesShortened} long lines (max reduction: ${stats.maxReduction} chars)`);
1297+
} else {
1298+
console.log(' ✅ No lines exceeded 60 characters');
1299+
}
1300+
1301+
// Write the line-broken version back to output file
1302+
writeFileSync(outputPath, JSON.stringify(processedPresentation, null, 2), 'utf-8');
1303+
13001304
// Copy to static directory for deployment
13011305
const staticPath = join(STATIC_OUTPUT_DIR, dirname(relativePath), outputFileName);
13021306
mkdirSync(dirname(staticPath), { recursive: true });
1303-
writeFileSync(staticPath, JSON.stringify(presentation, null, 2), 'utf-8');
1307+
writeFileSync(staticPath, JSON.stringify(processedPresentation, null, 2), 'utf-8');
13041308

13051309
// Update manifest
13061310
const presentationUrl = `/presentations/${join(dirname(relativePath), outputFileName)}`;
13071311
manifest[relativePath] = {
13081312
presentationUrl,
1309-
slideCount: presentation.slides.length,
1310-
estimatedDuration: presentation.metadata.estimatedDuration,
1311-
title: presentation.metadata.title,
1313+
slideCount: processedPresentation.slides.length,
1314+
estimatedDuration: processedPresentation.metadata.estimatedDuration,
1315+
title: processedPresentation.metadata.title,
13121316
generatedAt: new Date().toISOString()
13131317
};
13141318

13151319
console.log(` ✅ Generated: ${presentationUrl}`);
1316-
console.log(` 📊 Slides: ${presentation.slides.length}`);
1317-
console.log(` ⏱️ Duration: ${presentation.metadata.estimatedDuration}`);
1320+
console.log(` 📊 Slides: ${processedPresentation.slides.length}`);
1321+
console.log(` ⏱️ Duration: ${processedPresentation.metadata.estimatedDuration}`);
13181322

13191323
return outputPath;
13201324

scripts/lib/markdown-parser.js

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,10 @@ export function extractCodeSummary(code, language) {
120120
/**
121121
* Parse MDX/MD file and extract clean text content
122122
* @param {string} filePath - Path to MDX/MD file
123-
* @returns {string} Cleaned text content with code blocks described
123+
* @param {boolean} preserveCode - If true, keeps code blocks intact instead of converting to descriptions
124+
* @returns {string} Cleaned text content with code blocks described (or preserved if preserveCode=true)
124125
*/
125-
export function parseMarkdownContent(filePath) {
126+
export function parseMarkdownContent(filePath, preserveCode = false) {
126127
const content = readFileSync(filePath, 'utf-8');
127128

128129
// Remove frontmatter
@@ -135,42 +136,45 @@ export function parseMarkdownContent(filePath) {
135136
// Remove remaining HTML tags (lowercase = HTML elements)
136137
cleaned = cleaned.replace(/<[^>]+>/g, '');
137138

138-
// First pass: Find all code blocks and their contexts
139-
const codeBlocks = [];
140-
const regex = /```[\s\S]*?```/g;
141-
let match;
142-
143-
while ((match = regex.exec(cleaned)) !== null) {
144-
const precedingStart = Math.max(0, match.index - 200);
145-
const precedingContext = cleaned.substring(precedingStart, match.index);
146-
147-
const followingEnd = Math.min(cleaned.length, match.index + match[0].length + 200);
148-
const followingContext = cleaned.substring(match.index + match[0].length, followingEnd);
139+
// Code block processing: preserve or describe based on mode
140+
if (!preserveCode) {
141+
// First pass: Find all code blocks and their contexts
142+
const codeBlocks = [];
143+
const regex = /```[\s\S]*?```/g;
144+
let match;
145+
146+
while ((match = regex.exec(cleaned)) !== null) {
147+
const precedingStart = Math.max(0, match.index - 200);
148+
const precedingContext = cleaned.substring(precedingStart, match.index);
149+
150+
const followingEnd = Math.min(cleaned.length, match.index + match[0].length + 200);
151+
const followingContext = cleaned.substring(match.index + match[0].length, followingEnd);
152+
153+
codeBlocks.push({
154+
original: match[0],
155+
index: match.index,
156+
precedingContext,
157+
followingContext
158+
});
159+
}
149160

150-
codeBlocks.push({
151-
original: match[0],
152-
index: match.index,
153-
precedingContext,
154-
followingContext
155-
});
156-
}
161+
// Second pass: Replace code blocks with descriptions
162+
let offset = 0;
163+
for (const block of codeBlocks) {
164+
const description = describeCodeBlock(block.original, block.precedingContext, block.followingContext);
165+
const adjustedIndex = block.index + offset;
157166

158-
// Second pass: Replace code blocks with descriptions
159-
let offset = 0;
160-
for (const block of codeBlocks) {
161-
const description = describeCodeBlock(block.original, block.precedingContext, block.followingContext);
162-
const adjustedIndex = block.index + offset;
167+
cleaned = cleaned.substring(0, adjustedIndex) +
168+
description +
169+
cleaned.substring(adjustedIndex + block.original.length);
163170

164-
cleaned = cleaned.substring(0, adjustedIndex) +
165-
description +
166-
cleaned.substring(adjustedIndex + block.original.length);
171+
offset += description.length - block.original.length;
172+
}
167173

168-
offset += description.length - block.original.length;
174+
// Remove inline code (only when converting code blocks to descriptions)
175+
cleaned = cleaned.replace(/`[^`]+`/g, (match) => match.replace(/`/g, ''));
169176
}
170177

171-
// Remove inline code
172-
cleaned = cleaned.replace(/`[^`]+`/g, (match) => match.replace(/`/g, ''));
173-
174178
// Remove images
175179
cleaned = cleaned.replace(/!\[.*?\]\(.*?\)/g, '[Image]');
176180

0 commit comments

Comments
 (0)