Skip to content

Commit c4b28b7

Browse files
committed
Merge branch 'podcast'
2 parents f34ca87 + b9d6302 commit c4b28b7

File tree

12 files changed

+1292
-461
lines changed

12 files changed

+1292
-461
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,11 @@ node_modules/
3131
# AI Tools
3232
.chunkhound/
3333
.chunkhound.json
34+
35+
# Generated content
36+
scripts/output/
37+
38+
# Screenshots
39+
*.png
40+
*.jpg
41+
*.jpeg

scripts/generate-presentation.js

Lines changed: 288 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ PRESENTATION STRUCTURE REQUIREMENTS:
106106
- Discussion prompts or questions to ask students
107107
- Real-world examples to reference
108108
✓ DO: Preserve important code examples as slide content
109-
✓ DO: Identify which visual components to use (CapabilityMatrix, UShapeAttentionCurve, etc.)
109+
✓ DO: Identify which visual components to use (CapabilityMatrix, UShapeAttentionCurve, WorkflowCircle, GroundingComparison, ContextWindowMeter, AbstractShapesVisualization, etc.)
110110
111111
✗ AVOID: Long paragraphs on slides (slides are visual anchors, not reading material)
112112
✗ AVOID: More than 5 bullet points per slide
@@ -119,9 +119,10 @@ SLIDE TYPES:
119119
1. **Title Slide**: Lesson title, learning objectives
120120
2. **Concept Slide**: Key idea with 3-5 bullet points
121121
3. **Code Example Slide**: Code snippet with context
122-
4. **Comparison Slide**: Effective vs ineffective patterns
123-
5. **Visual Slide**: Custom component (CapabilityMatrix, etc.)
124-
6. **Key Takeaway Slide**: Summary of section or lesson
122+
4. **Code Execution Slide**: Step-by-step visualization of execution flows (agent loops, algorithms, workflows)
123+
5. **Comparison Slide**: Effective vs ineffective patterns
124+
6. **Visual Slide**: Custom component (CapabilityMatrix, etc.)
125+
7. **Key Takeaway Slide**: Summary of section or lesson
125126
126127
HANDLING CODE BLOCKS:
127128
@@ -136,9 +137,106 @@ For presentation slides:
136137
✓ Add context in speaker notes about what the code demonstrates
137138
✓ For comparison slides, show ineffective and effective side-by-side
138139
✓ Keep code snippets under 15 lines for readability
140+
✓ EXCEPTION: Textual context flow examples showing agent conversation flows should use "codeExecution" slide type regardless of length (see section below)
139141
✗ Don't include every code example from the lesson
140142
✗ Don't show code without explaining its purpose
141143
144+
COMPONENT DETECTION (CRITICAL):
145+
146+
The source content contains markers for visual React components in the format:
147+
[VISUAL_COMPONENT: ComponentName]
148+
149+
Examples you will see:
150+
- [VISUAL_COMPONENT: AbstractShapesVisualization]
151+
- [VISUAL_COMPONENT: CapabilityMatrix]
152+
- [VISUAL_COMPONENT: UShapeAttentionCurve]
153+
- [VISUAL_COMPONENT: ContextWindowMeter]
154+
155+
**MANDATORY RULE:** When you encounter a [VISUAL_COMPONENT: X] marker, you MUST:
156+
1. Generate a "visual" slide type (NOT a "concept" slide)
157+
2. Set "component" field to the exact component name from the marker
158+
3. Use the surrounding context to write a descriptive caption
159+
160+
Example:
161+
{
162+
"type": "visual",
163+
"component": "AbstractShapesVisualization",
164+
"caption": "Visual comparison showing cluttered vs clean context"
165+
}
166+
167+
**DO NOT:**
168+
- Convert component markers into text bullet points
169+
- Skip component markers
170+
- Change the component name
171+
- Generate a "concept" slide when you see a component marker
172+
173+
If you see [VISUAL_COMPONENT: X] anywhere in the content, it MUST become a visual slide.
174+
175+
CODE EXECUTION SLIDES:
176+
177+
Use the "codeExecution" slide type to visualize step-by-step processes like:
178+
- Agent execution loops (human input → LLM prediction → agent execution → feedback)
179+
- Algorithm execution flows
180+
- Request/response cycles
181+
- Multi-step workflows
182+
183+
Structure with highlightType for semantic color-coding (uses design system colors):
184+
- **"human"** (white/neutral): Engineer/operator input, commands, task specifications, explicit constraints
185+
- **"prediction"** (purple): LLM predictions, reasoning, decisions, "I will..." or "I should..." statements
186+
- **"execution"** (green): Agent/software tool calls, deterministic actions (Read, Edit, Bash commands)
187+
- **"feedback"** (purple light): Data/results returned from operations, outputs that LLM receives
188+
- **"summary"** (white/neutral): Loop conditions, conclusions, final outcomes
189+
190+
SEMANTIC RULES (critical for correct color coding):
191+
✓ "Engineer specifies task:" → human (operator input)
192+
✓ "LLM predicts:" or "LLM decides:" → prediction (thinking/planning)
193+
✓ "Agent executes: Read(...)" → execution (tool call)
194+
✓ "File content returned:" → feedback (operation result)
195+
✓ "LLM receives and predicts:" → prediction (NOT feedback - it's the prediction after receiving)
196+
✓ "Loop continues until..." → summary (loop condition)
197+
198+
✓ Use for "how it works" explanations (3-8 steps typical)
199+
✓ Include annotations to explain WHY each step happens
200+
✓ Show the complete cycle from start to finish
201+
✓ Maintain semantic consistency: what's DOING the action determines the type
202+
✗ Don't use for static code examples (use "code" type instead)
203+
✗ Don't create more than 10 steps (split into multiple slides if needed)
204+
✗ Don't confuse "LLM receives data and predicts" (prediction) with "data returned" (feedback)
205+
206+
RECOGNIZING TEXTUAL CONTEXT FLOW PATTERNS (CRITICAL):
207+
208+
When you see code blocks showing conversation/execution flows with patterns like:
209+
- "SYSTEM: ... USER: ... ASSISTANT: ... TOOL_RESULT: ..."
210+
- Sequential back-and-forth between human, LLM, and tools
211+
- Full execution traces showing how text flows through agent context
212+
- Examples demonstrating the actual content of the context window
213+
214+
→ These are PEDAGOGICALLY CRITICAL and must be included as "codeExecution" slides
215+
216+
Why these matter MORE than config examples:
217+
- They show the fundamental mental model of how agents operate
218+
- They demystify what "context" actually contains
219+
- They're the core learning insight, not just implementation details
220+
221+
How to handle them:
222+
1. Break the flow into 8-12 logical steps (not necessarily every line)
223+
2. Map conversation elements to highlightTypes:
224+
- "SYSTEM:" or system instructions → human
225+
- "USER:" or task specification → human
226+
- "ASSISTANT:" thinking/reasoning → prediction
227+
- "<tool_use>" or tool calls → execution
228+
- "TOOL_RESULT:" or outputs → feedback
229+
3. Add annotations explaining the significance of each step
230+
4. Focus on the FLOW of text through the context, not just the code
231+
232+
Example transformation:
233+
- Source: 67-line conversation showing full agent execution
234+
- Slide: 10 steps highlighting key moments in the conversation flow
235+
- Annotations: "Notice how the tool result becomes input to the next prediction"
236+
237+
PRIORITIZATION: Textual flow examples showing context mechanics trump configuration
238+
examples like MCP setup. Configuration is implementation; textual flow is understanding.
239+
142240
SPEAKER NOTES GUIDELINES:
143241
144242
For each slide, provide speaker notes with:
@@ -204,23 +302,97 @@ You must generate a valid JSON file with this structure:
204302
"caption": "Brief explanation",
205303
"speakerNotes": { ... }
206304
},
305+
{
306+
"type": "codeExecution",
307+
"title": "Agent Execution Loop Example",
308+
"steps": [
309+
{
310+
"line": "Engineer specifies: 'Add authentication middleware'",
311+
"highlightType": "human",
312+
"annotation": "Human provides explicit task and constraints"
313+
},
314+
{
315+
"line": "LLM predicts: 'I should read existing auth patterns'",
316+
"highlightType": "prediction",
317+
"annotation": "Token prediction drives next action"
318+
},
319+
{
320+
"line": "Agent executes: Read(src/middleware/auth.ts)",
321+
"highlightType": "execution",
322+
"annotation": "Deterministic tool execution"
323+
},
324+
{
325+
"line": "File content returned to context",
326+
"highlightType": "feedback",
327+
"annotation": "Operation result available to LLM"
328+
},
329+
{
330+
"line": "LLM analyzes patterns and predicts: 'I'll use JWT approach'",
331+
"highlightType": "prediction",
332+
"annotation": "Prediction incorporates new context"
333+
},
334+
{
335+
"line": "Agent executes: Edit(src/app.ts, old, new)",
336+
"highlightType": "execution",
337+
"annotation": "Code modification"
338+
},
339+
{
340+
"line": "Loop continues until tests pass",
341+
"highlightType": "summary",
342+
"annotation": "Iteration condition"
343+
}
344+
],
345+
"speakerNotes": { ... }
346+
},
347+
348+
COMPARISON SLIDE CONVENTION (CRITICAL - HARDCODED IN UI):
349+
350+
The comparison slide type has HARDCODED styling in the presentation component:
351+
- LEFT side → RED background, RED heading, ✗ icons (ineffective/worse/limited)
352+
- RIGHT side → GREEN background, GREEN heading, ✓ icons (effective/better/superior)
353+
354+
YOU MUST ALWAYS follow this convention:
355+
- LEFT: The worse/ineffective/traditional/limited approach
356+
- RIGHT: The better/effective/modern/superior approach
357+
358+
Correct examples:
359+
- "Chat Interface" (left) vs "Agent Workflow" (right)
360+
- "Heavy Mocking" (left) vs "Sociable Tests" (right)
361+
- "Chat/IDE Agents" (left) vs "CLI Agents" (right)
362+
- "Traditional RAG" (left) vs "Agentic RAG" (right)
363+
364+
INCORRECT: Putting the better option on the left will show it with RED ✗ styling!
365+
207366
{
208367
"type": "comparison",
209368
"title": "Ineffective vs Effective",
210369
"left": {
211-
"label": "Ineffective",
370+
"label": "Ineffective", // MANDATORY: LEFT = worse/ineffective/limited (RED ✗)
212371
"content": ["Point 1", "Point 2"]
213372
},
214373
"right": {
215-
"label": "Effective",
374+
"label": "Effective", // MANDATORY: RIGHT = better/effective/superior (GREEN ✓)
216375
"content": ["Point 1", "Point 2"]
217376
},
218377
"speakerNotes": { ... }
219378
},
379+
{
380+
"type": "marketingReality",
381+
"title": "Marketing vs Reality: What Actually Happens",
382+
"metaphor": {
383+
"label": "Marketing Speak",
384+
"content": ["Metaphorical statement 1", "Metaphorical statement 2"]
385+
},
386+
"reality": {
387+
"label": "Technical Reality",
388+
"content": ["Technical explanation 1", "Technical explanation 2"]
389+
},
390+
"speakerNotes": { ... }
391+
},
220392
{
221393
"type": "visual",
222394
"title": "Visual Component",
223-
"component": "CapabilityMatrix",
395+
"component": "CapabilityMatrix | UShapeAttentionCurve | WorkflowCircle | GroundingComparison | ContextWindowMeter | AbstractShapesVisualization",
224396
"caption": "Description of what the visual shows",
225397
"speakerNotes": { ... }
226398
},
@@ -420,6 +592,90 @@ async function promptSelectFile(files, baseDir) {
420592
// PROCESSING
421593
// ============================================================================
422594

595+
/**
596+
* Extract visual component names from parsed content
597+
* @param {string} content - Parsed markdown content
598+
* @returns {string[]} Array of component names
599+
*/
600+
function extractExpectedComponents(content) {
601+
const componentRegex = /\[VISUAL_COMPONENT: ([A-Za-z]+)\]/g;
602+
const components = [];
603+
let match;
604+
605+
while ((match = componentRegex.exec(content)) !== null) {
606+
components.push(match[1]);
607+
}
608+
609+
return components;
610+
}
611+
612+
/**
613+
* Validate that all expected visual components appear in the presentation
614+
* @param {string} content - Parsed markdown content
615+
* @param {object} presentation - Generated presentation object
616+
* @returns {object} Validation result with missing components
617+
*/
618+
function validateComponents(content, presentation) {
619+
const expectedComponents = extractExpectedComponents(content);
620+
const visualSlides = presentation.slides.filter(s => s.type === 'visual');
621+
const renderedComponents = visualSlides.map(s => s.component);
622+
623+
const missing = expectedComponents.filter(c => !renderedComponents.includes(c));
624+
625+
return {
626+
expected: expectedComponents,
627+
rendered: renderedComponents,
628+
missing,
629+
allPresent: missing.length === 0
630+
};
631+
}
632+
633+
/**
634+
* Validate semantic correctness of comparison slides
635+
* Checks that better/effective options are on the RIGHT (green ✓)
636+
* and worse/ineffective options are on the LEFT (red ✗)
637+
* @param {object} presentation - Generated presentation object
638+
* @returns {object} Validation result with potential ordering issues
639+
*/
640+
function validateComparisonSemantics(presentation) {
641+
const comparisonSlides = presentation.slides.filter(s => s.type === 'comparison');
642+
const issues = [];
643+
644+
// Keywords that indicate a "positive/better" option
645+
const positiveKeywords = ['cli', 'effective', 'better', 'modern', 'agentic', 'sociable', 'agent workflow'];
646+
// Keywords that indicate a "negative/worse" option
647+
const negativeKeywords = ['chat', 'ide', 'ineffective', 'worse', 'traditional', 'mocked', 'chat interface'];
648+
649+
for (const slide of comparisonSlides) {
650+
if (!slide.left || !slide.right) continue;
651+
652+
const leftLabel = slide.left.label?.toLowerCase() || '';
653+
const rightLabel = slide.right.label?.toLowerCase() || '';
654+
655+
// Check if left side has positive keywords (should be on right instead)
656+
const leftIsPositive = positiveKeywords.some(k => leftLabel.includes(k));
657+
// Check if right side has negative keywords (should be on left instead)
658+
const rightIsNegative = negativeKeywords.some(k => rightLabel.includes(k));
659+
660+
if (leftIsPositive || rightIsNegative) {
661+
issues.push({
662+
slide: slide.title,
663+
left: slide.left.label,
664+
right: slide.right.label,
665+
reason: leftIsPositive
666+
? `"${slide.left.label}" appears positive/better but is on LEFT (will show RED ✗)`
667+
: `"${slide.right.label}" appears negative/worse but is on RIGHT (will show GREEN ✓)`
668+
});
669+
}
670+
}
671+
672+
return {
673+
valid: issues.length === 0,
674+
issues,
675+
totalComparisons: comparisonSlides.length
676+
};
677+
}
678+
423679
/**
424680
* Generate presentation for a file
425681
*/
@@ -462,6 +718,31 @@ async function generatePresentation(filePath, manifest, config) {
462718
// Generate presentation using Claude
463719
const presentation = await generatePresentationWithClaude(prompt, outputPath);
464720

721+
// Validate that all visual components were included
722+
const validation = validateComponents(content, presentation);
723+
if (!validation.allPresent) {
724+
console.log(` ⚠️ WARNING: ${validation.missing.length} visual component(s) not rendered:`);
725+
validation.missing.forEach(c => console.log(` - ${c}`));
726+
console.log(` ℹ️ Expected: [${validation.expected.join(', ')}]`);
727+
console.log(` ℹ️ Rendered: [${validation.rendered.join(', ')}]`);
728+
} else if (validation.expected.length > 0) {
729+
console.log(` ✅ All ${validation.expected.length} visual component(s) rendered correctly`);
730+
}
731+
732+
// Validate comparison slide semantics
733+
const semanticValidation = validateComparisonSemantics(presentation);
734+
if (!semanticValidation.valid) {
735+
console.log(` ⚠️ WARNING: ${semanticValidation.issues.length} comparison slide(s) may have reversed order:`);
736+
semanticValidation.issues.forEach(issue => {
737+
console.log(` - "${issue.slide}"`);
738+
console.log(` LEFT: "${issue.left}" | RIGHT: "${issue.right}"`);
739+
console.log(` ${issue.reason}`);
740+
});
741+
console.log(` ℹ️ Remember: LEFT = ineffective/worse (RED ✗), RIGHT = effective/better (GREEN ✓)`);
742+
} else if (semanticValidation.totalComparisons > 0) {
743+
console.log(` ✅ All ${semanticValidation.totalComparisons} comparison slide(s) follow correct convention`);
744+
}
745+
465746
// Copy to static directory for deployment
466747
const staticPath = join(STATIC_OUTPUT_DIR, dirname(relativePath), outputFileName);
467748
mkdirSync(dirname(staticPath), { recursive: true });

scripts/lib/markdown-parser.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ export function parseMarkdownContent(filePath) {
128128
// Remove frontmatter
129129
let cleaned = content.replace(/^---[\s\S]*?---\n/, '');
130130

131-
// Remove JSX components
131+
// Extract and preserve React components (capital letter start = React components)
132+
// Replace with clear markers so LLM can detect them
133+
cleaned = cleaned.replace(/<([A-Z][a-zA-Z]*)\s*\/>/g, '[VISUAL_COMPONENT: $1]');
134+
135+
// Remove remaining HTML tags (lowercase = HTML elements)
132136
cleaned = cleaned.replace(/<[^>]+>/g, '');
133137

134138
// First pass: Find all code blocks and their contexts

0 commit comments

Comments
 (0)