Skip to content

Commit 0bdaa5f

Browse files
committed
Proper handling of code blocks
1 parent 1273034 commit 0bdaa5f

24 files changed

+866
-555
lines changed

scripts/generate-podcast.js

Lines changed: 236 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,163 @@ function parseArgs() {
8686
// SCRIPT GENERATION - From generate-podcast-script.js
8787
// ============================================================================
8888

89+
/**
90+
* Analyze code block context and generate audio-appropriate description
91+
* Transforms code into natural language that preserves pedagogical value
92+
*/
93+
function describeCodeBlock(codeBlock, precedingContext, followingContext) {
94+
// Extract language and code content
95+
const langMatch = codeBlock.match(/```(\w+)?\n([\s\S]*?)```/);
96+
const language = langMatch?.[1] || '';
97+
const code = langMatch?.[2]?.trim() || '';
98+
99+
if (!code) {
100+
return '[Code example]';
101+
}
102+
103+
// Focus on immediate context (last 100 chars before, first 100 chars after)
104+
const immediatePreContext = precedingContext.substring(Math.max(0, precedingContext.length - 100));
105+
const immediatePostContext = followingContext.substring(0, 100);
106+
107+
// Detect code block type based on context and content
108+
const fullContext = (precedingContext + ' ' + followingContext).toLowerCase();
109+
const immediateContext = (immediatePreContext + ' ' + immediatePostContext).toLowerCase();
110+
111+
// Type 1: Comparison examples (ineffective vs effective)
112+
// Prioritize immediate context for these labels
113+
if (immediateContext.includes('**ineffective:**') || immediateContext.includes('**risky:**') ||
114+
immediateContext.includes('**bad:**') || immediateContext.includes('**wrong:**') ||
115+
immediateContext.includes('**don\'t rely on llms')) {
116+
return `[INEFFECTIVE CODE EXAMPLE: ${extractCodeSummary(code, language)}]`;
117+
}
118+
119+
if (immediateContext.includes('**effective:**') || immediateContext.includes('**better:**') ||
120+
immediateContext.includes('**good:**') || immediateContext.includes('**correct:**') ||
121+
immediateContext.includes('**instead')) {
122+
return `[EFFECTIVE CODE EXAMPLE: ${extractCodeSummary(code, language)}]`;
123+
}
124+
125+
// Fallback to emojis and broader context if no immediate label found
126+
if (fullContext.includes('❌') && !immediateContext.includes('✅')) {
127+
return `[INEFFECTIVE CODE EXAMPLE: ${extractCodeSummary(code, language)}]`;
128+
}
129+
130+
if (fullContext.includes('✅') && !immediateContext.includes('❌')) {
131+
return `[EFFECTIVE CODE EXAMPLE: ${extractCodeSummary(code, language)}]`;
132+
}
133+
134+
// Type 2: Pattern demonstrations (showing structure)
135+
if (fullContext.includes('pattern') || fullContext.includes('structure') ||
136+
immediateContext.includes('example') || fullContext.includes('template')) {
137+
return `[CODE PATTERN: ${extractCodeSummary(code, language)}]`;
138+
}
139+
140+
// Type 3: Specifications with requirements (numbered lists, constraints)
141+
if (code.includes('\n-') || code.includes('\n•') ||
142+
/\d+\./.test(code) || fullContext.includes('requirement') ||
143+
fullContext.includes('constraint') || fullContext.includes('specification')) {
144+
const requirements = extractRequirements(code);
145+
return `[CODE SPECIFICATION: ${extractCodeSummary(code, language)}. ${requirements}]`;
146+
}
147+
148+
// Type 4: Default - describe the code structure
149+
return `[CODE EXAMPLE: ${extractCodeSummary(code, language)}]`;
150+
}
151+
152+
/**
153+
* Extract a concise summary of what the code does/shows
154+
*/
155+
function extractCodeSummary(code, language) {
156+
const lines = code.split('\n').filter(l => l.trim());
157+
158+
// Detect function definitions - more precise matching
159+
const functionMatch = code.match(/(?:^|\n)\s*(?:export\s+)?(?:async\s+)?(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?function)/m);
160+
if (functionMatch) {
161+
const funcName = functionMatch[1] || functionMatch[2] || functionMatch[3];
162+
// Validate it's a real function name (at least 3 chars, starts with letter)
163+
if (funcName && funcName.length >= 3 && /^[a-zA-Z]/.test(funcName)) {
164+
const params = code.match(/\(([^)]*)\)/)?.[1] || '';
165+
const paramCount = params.trim() ? params.split(',').length : 0;
166+
const hasReturn = code.includes('return');
167+
return `Function '${funcName}'${paramCount > 0 ? ` with ${paramCount} parameter${paramCount > 1 ? 's' : ''}` : ''}${hasReturn ? ' that returns a value' : ''}`;
168+
}
169+
}
170+
171+
// Detect type/interface definitions
172+
if (code.includes('interface') || code.includes('type')) {
173+
const typeMatch = code.match(/(?:interface|type)\s+(\w+)/);
174+
if (typeMatch) {
175+
return `Type definition '${typeMatch[1]}'`;
176+
}
177+
}
178+
179+
// Detect class definitions
180+
if (code.includes('class')) {
181+
const classMatch = code.match(/class\s+(\w+)/);
182+
if (classMatch) {
183+
return `Class '${classMatch[1]}'`;
184+
}
185+
}
186+
187+
// Detect imports
188+
if (code.includes('import') || code.includes('require')) {
189+
return 'Import statements for dependencies';
190+
}
191+
192+
// Detect configuration/object
193+
if (code.trim().startsWith('{') || code.includes('config') || code.includes('options')) {
194+
return 'Configuration object with properties';
195+
}
196+
197+
// Detect command-line/shell
198+
if (language === 'bash' || language === 'sh' || code.includes('$') || code.includes('npm') || code.includes('git')) {
199+
const commands = lines.filter(l => !l.startsWith('#')).length;
200+
return `Shell command${commands > 1 ? 's' : ''} (${commands} line${commands > 1 ? 's' : ''})`;
201+
}
202+
203+
// Detect specifications/requirements
204+
if (code.includes('-') || code.includes('•') || /^\d+\./.test(code)) {
205+
const items = lines.filter(l => l.match(/^[\s-\d]/)).length;
206+
return `Specification with ${items} requirement${items > 1 ? 's' : ''}`;
207+
}
208+
209+
// Default: describe by line count and language
210+
const lineCount = lines.length;
211+
return `${language || 'Code'} snippet (${lineCount} line${lineCount > 1 ? 's' : ''})`;
212+
}
213+
214+
/**
215+
* Extract and summarize requirements from code
216+
*/
217+
function extractRequirements(code) {
218+
const lines = code.split('\n');
219+
const requirements = [];
220+
221+
// Extract lines that look like requirements (bullets, numbers, dashes)
222+
for (const line of lines) {
223+
const trimmed = line.trim();
224+
if (trimmed.match(/^[-\d]+[.)]?\s+(.+)/) && trimmed.length > 5) {
225+
const content = trimmed.replace(/^[-\d]+[.)]?\s+/, '').trim();
226+
if (content.length > 0) {
227+
requirements.push(content);
228+
}
229+
}
230+
}
231+
232+
if (requirements.length === 0) {
233+
return '';
234+
}
235+
236+
if (requirements.length <= 3) {
237+
return `Requirements: ${requirements.join('; ')}`;
238+
}
239+
240+
return `${requirements.length} specific requirements including: ${requirements.slice(0, 2).join('; ')}`;
241+
}
242+
89243
/**
90244
* Parse MDX/MD file and extract clean text content
91-
* Strips frontmatter, JSX components, and code blocks
245+
* Transforms code blocks into audio-appropriate descriptions
92246
*/
93247
function parseMarkdownContent(filePath) {
94248
const content = readFileSync(filePath, 'utf-8');
@@ -99,10 +253,39 @@ function parseMarkdownContent(filePath) {
99253
// Remove JSX components (simple approach - remove anything with <>)
100254
cleaned = cleaned.replace(/<[^>]+>/g, '');
101255

102-
// Extract text from code blocks but label them
103-
cleaned = cleaned.replace(/```[\s\S]*?```/g, () => {
104-
return '[Code example omitted for audio]';
105-
});
256+
// First pass: Find all code blocks and their contexts BEFORE transformation
257+
// This prevents context pollution from replaced blocks
258+
const codeBlocks = [];
259+
const regex = /```[\s\S]*?```/g;
260+
let match;
261+
262+
while ((match = regex.exec(cleaned)) !== null) {
263+
const precedingStart = Math.max(0, match.index - 200);
264+
const precedingContext = cleaned.substring(precedingStart, match.index);
265+
266+
const followingEnd = Math.min(cleaned.length, match.index + match[0].length + 200);
267+
const followingContext = cleaned.substring(match.index + match[0].length, followingEnd);
268+
269+
codeBlocks.push({
270+
original: match[0],
271+
index: match.index,
272+
precedingContext,
273+
followingContext
274+
});
275+
}
276+
277+
// Second pass: Replace code blocks with descriptions
278+
let offset = 0;
279+
for (const block of codeBlocks) {
280+
const description = describeCodeBlock(block.original, block.precedingContext, block.followingContext);
281+
const adjustedIndex = block.index + offset;
282+
283+
cleaned = cleaned.substring(0, adjustedIndex) +
284+
description +
285+
cleaned.substring(adjustedIndex + block.original.length);
286+
287+
offset += description.length - block.original.length;
288+
}
106289

107290
// Remove inline code
108291
cleaned = cleaned.replace(/`[^`]+`/g, (match) => match.replace(/`/g, ''));
@@ -181,6 +364,54 @@ DIALOG STYLE REQUIREMENTS:
181364
✗ AVOID: Dumbing down technical concepts
182365
✗ AVOID: Marketing language or hype
183366
367+
CRITICAL: HANDLING CODE BLOCKS IN AUDIO FORMAT
368+
369+
The source material includes code blocks that have been transformed into audio-appropriate descriptions with tags like:
370+
- [INEFFECTIVE CODE EXAMPLE: ...] - Shows what NOT to do
371+
- [EFFECTIVE CODE EXAMPLE: ...] - Shows the correct approach
372+
- [CODE PATTERN: ...] - Demonstrates a structure or template
373+
- [CODE SPECIFICATION: ...] - Lists requirements or constraints
374+
- [CODE EXAMPLE: ...] - General code reference
375+
376+
HOW TO NARRATE CODE IN DIALOG:
377+
378+
✓ DO: Narrate the code's structure and intent in natural language
379+
Example: "The effective version is a TypeScript function called validateEmail that takes an email string and returns an object with a valid boolean and optional reason. It has three specific constraints: reject multiple @ symbols, reject missing domains, and accept plus addressing."
380+
381+
✓ DO: Explain what the code demonstrates pedagogically
382+
Example: "This code pattern shows how starting with a function signature and type annotations helps the AI understand exactly what you want."
383+
384+
✓ DO: Connect code to the conversational point being made
385+
Example: "See the difference? The ineffective prompt just says 'validate emails', but the effective version specifies the RFC standard, lists edge cases, and defines the return structure."
386+
387+
✓ DO: Use code as concrete examples to back abstract discussions
388+
Example: "When I say 'be specific', here's what I mean: instead of 'handle errors', write 'throw ValidationError with a descriptive message for each of these five cases'."
389+
390+
✓ DO: Compare code blocks when showing ineffective vs effective patterns
391+
Example: "The first version gives no context, just 'Write a function that validates emails.' The second version specifies TypeScript, references RFC 5322, lists three edge cases, and defines the exact return type."
392+
393+
✗ DO NOT: Skip over code blocks - they're pedagogically important
394+
✗ DO NOT: Say "there's a code example here" without describing what it shows
395+
✗ DO NOT: Lose the specificity that the code demonstrates
396+
✗ DO NOT: Read code character-by-character or line-by-line
397+
✗ DO NOT: Use phrases like "the code shows" without explaining WHAT it shows
398+
399+
COMPARING EFFECTIVE VS INEFFECTIVE CODE:
400+
When you see both [INEFFECTIVE CODE EXAMPLE: ...] and [EFFECTIVE CODE EXAMPLE: ...]:
401+
1. First, describe what's wrong with the ineffective version (vague, missing context, unclear requirements)
402+
2. Then, contrast with the effective version (specific, constrained, clear expectations)
403+
3. Explain WHY the effective version works better (helps AI understand intent, reduces ambiguity, produces better results)
404+
4. Make the contrast explicit and compelling
405+
406+
EXAMPLE DIALOG FOR CODE BLOCKS:
407+
Alex: "Let me show you the difference between an ineffective and effective prompt. The ineffective one just says 'Write a function that validates emails.' That's it. No language, no specification, no edge cases."
408+
409+
Sam: "So the AI has to guess everything - which TypeScript or Python, which validation standard, how to handle plus addressing..."
410+
411+
Alex: "Exactly. Now look at the effective version: 'Write a TypeScript function that validates email addresses per RFC 5322. Handle these edge cases: reject multiple @ symbols, reject missing domains, accept plus addressing. Return an object with a valid boolean and optional reason field.' See how much clearer that is?"
412+
413+
Sam: "That's night and day. The second version gives the AI everything it needs - language, standard, edge cases, return type."
414+
184415
CRITICAL: CONTENT DEDUPLICATION REQUIREMENTS
185416
186417
The source material uses pedagogical reinforcement patterns designed for written learning:

0 commit comments

Comments
 (0)