claude-presentation-master 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ __export(index_exports, {
34
34
  CompositeChartProvider: () => CompositeChartProvider,
35
35
  CompositeImageProvider: () => CompositeImageProvider,
36
36
  ContentAnalyzer: () => ContentAnalyzer,
37
- KnowledgeBase: () => KnowledgeBase,
37
+ KnowledgeGateway: () => KnowledgeGateway,
38
38
  LocalImageProvider: () => LocalImageProvider,
39
39
  MermaidProvider: () => MermaidProvider,
40
40
  PlaceholderImageProvider: () => PlaceholderImageProvider,
@@ -55,7 +55,7 @@ __export(index_exports, {
55
55
  createDefaultImageProvider: () => createDefaultImageProvider,
56
56
  default: () => index_default,
57
57
  generate: () => generate,
58
- getKnowledgeBase: () => getKnowledgeBase,
58
+ getKnowledgeGateway: () => getKnowledgeGateway,
59
59
  validate: () => validate
60
60
  });
61
61
  module.exports = __toCommonJS(index_exports);
@@ -88,8 +88,281 @@ var TemplateNotFoundError = class extends Error {
88
88
  }
89
89
  };
90
90
 
91
+ // src/kb/KnowledgeGateway.ts
92
+ var import_fs = require("fs");
93
+ var import_path = require("path");
94
+ var import_url = require("url");
95
+ var yaml = __toESM(require("yaml"));
96
+ var import_meta = {};
97
+ function getModuleDir() {
98
+ if (typeof import_meta !== "undefined" && import_meta.url) {
99
+ return (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
100
+ }
101
+ if (typeof __dirname !== "undefined") {
102
+ return __dirname;
103
+ }
104
+ return process.cwd();
105
+ }
106
+ var KnowledgeGateway = class {
107
+ kb;
108
+ loaded = false;
109
+ constructor() {
110
+ }
111
+ /**
112
+ * Load the knowledge base from YAML file
113
+ */
114
+ async load() {
115
+ if (this.loaded) return;
116
+ const moduleDir = getModuleDir();
117
+ const possiblePaths = [
118
+ (0, import_path.join)(moduleDir, "../../assets/presentation-knowledge.yaml"),
119
+ (0, import_path.join)(moduleDir, "../assets/presentation-knowledge.yaml"),
120
+ (0, import_path.join)(moduleDir, "../../../assets/presentation-knowledge.yaml"),
121
+ (0, import_path.join)(moduleDir, "assets/presentation-knowledge.yaml"),
122
+ (0, import_path.join)(process.cwd(), "assets/presentation-knowledge.yaml"),
123
+ (0, import_path.join)(process.cwd(), "node_modules/claude-presentation-master/assets/presentation-knowledge.yaml")
124
+ ];
125
+ for (const path of possiblePaths) {
126
+ try {
127
+ const content = (0, import_fs.readFileSync)(path, "utf-8");
128
+ this.kb = yaml.parse(content);
129
+ this.loaded = true;
130
+ console.log(` \u2713 Knowledge base loaded from ${path}`);
131
+ return;
132
+ } catch {
133
+ }
134
+ }
135
+ throw new Error(`Knowledge base not found. Tried: ${possiblePaths.join(", ")}`);
136
+ }
137
+ /**
138
+ * Get presentation mode configuration (keynote or business)
139
+ */
140
+ getMode(mode) {
141
+ this.ensureLoaded();
142
+ return this.kb.presentation_modes[mode];
143
+ }
144
+ /**
145
+ * Determine which mode is best for a given presentation type
146
+ */
147
+ getModeForType(type) {
148
+ const keynoteTypes = ["ted_keynote", "sales_pitch", "all_hands"];
149
+ const businessTypes = ["consulting_deck", "investment_banking", "investor_pitch", "technical_presentation"];
150
+ if (keynoteTypes.includes(type)) return "keynote";
151
+ if (businessTypes.includes(type)) return "business";
152
+ return "keynote";
153
+ }
154
+ /**
155
+ * Get word limits for the specified mode
156
+ */
157
+ getWordLimits(mode) {
158
+ const modeConfig = this.getMode(mode);
159
+ const range = modeConfig.characteristics.words_per_slide;
160
+ if (mode === "keynote") {
161
+ return { min: 6, max: 15, ideal: 10 };
162
+ } else {
163
+ return { min: 40, max: 80, ideal: 60 };
164
+ }
165
+ }
166
+ /**
167
+ * Get bullet point limits
168
+ */
169
+ getBulletLimits(mode) {
170
+ if (mode === "keynote") {
171
+ return { maxBullets: 3, maxWordsPerBullet: 8 };
172
+ } else {
173
+ return { maxBullets: 5, maxWordsPerBullet: 20 };
174
+ }
175
+ }
176
+ /**
177
+ * Get whitespace percentage requirement
178
+ */
179
+ getWhitespaceRequirement(mode) {
180
+ if (mode === "keynote") return 0.4;
181
+ return 0.25;
182
+ }
183
+ /**
184
+ * Get slide templates for the specified mode
185
+ */
186
+ getSlideTemplates(mode) {
187
+ this.ensureLoaded();
188
+ const key = mode === "keynote" ? "keynote_mode" : "business_mode";
189
+ return this.kb.slide_templates[key] || [];
190
+ }
191
+ /**
192
+ * Get a specific slide template by name
193
+ */
194
+ getSlideTemplate(mode, templateName) {
195
+ const templates = this.getSlideTemplates(mode);
196
+ return templates.find((t) => t.name.toLowerCase().includes(templateName.toLowerCase()));
197
+ }
198
+ /**
199
+ * Get quality metrics for grading
200
+ */
201
+ getQualityMetrics() {
202
+ this.ensureLoaded();
203
+ return this.kb.quality_metrics;
204
+ }
205
+ /**
206
+ * Get anti-patterns to avoid
207
+ */
208
+ getAntiPatterns(mode) {
209
+ this.ensureLoaded();
210
+ const key = mode === "keynote" ? "keynote_mode" : "business_mode";
211
+ return this.kb.anti_patterns[key] || [];
212
+ }
213
+ /**
214
+ * Get data visualization anti-patterns
215
+ */
216
+ getDataVizAntiPatterns() {
217
+ this.ensureLoaded();
218
+ return this.kb.anti_patterns.data_visualization || [];
219
+ }
220
+ /**
221
+ * Get accessibility anti-patterns
222
+ */
223
+ getAccessibilityAntiPatterns() {
224
+ this.ensureLoaded();
225
+ return this.kb.anti_patterns.accessibility || [];
226
+ }
227
+ /**
228
+ * Get color palette by name
229
+ */
230
+ getColorPalette(name) {
231
+ this.ensureLoaded();
232
+ const palettes = this.kb.design_system.color_palettes;
233
+ return palettes[name];
234
+ }
235
+ /**
236
+ * Get recommended color palette for presentation type
237
+ */
238
+ getRecommendedPalette(type) {
239
+ this.ensureLoaded();
240
+ const palettes = this.kb.design_system.color_palettes;
241
+ switch (type) {
242
+ case "consulting_deck":
243
+ return palettes.consulting_classic;
244
+ case "investment_banking":
245
+ case "investor_pitch":
246
+ return palettes.executive_professional;
247
+ case "ted_keynote":
248
+ return palettes.dark_executive;
249
+ case "sales_pitch":
250
+ return palettes.modern_business;
251
+ case "technical_presentation":
252
+ return palettes.modern_business;
253
+ case "all_hands":
254
+ return palettes.strategy_growth;
255
+ default:
256
+ return palettes.consulting_classic;
257
+ }
258
+ }
259
+ /**
260
+ * Get typography guidelines for mode
261
+ */
262
+ getTypography(mode) {
263
+ this.ensureLoaded();
264
+ const key = mode === "keynote" ? "keynote_mode" : "business_mode";
265
+ return this.kb.design_system.typography[key];
266
+ }
267
+ /**
268
+ * Get story framework by name
269
+ */
270
+ getStoryFramework(name) {
271
+ this.ensureLoaded();
272
+ return this.kb.story_frameworks[name];
273
+ }
274
+ /**
275
+ * Get expert principles by name
276
+ */
277
+ getExpertPrinciples(expertName) {
278
+ this.ensureLoaded();
279
+ if (this.kb.experts[expertName]) {
280
+ return this.kb.experts[expertName];
281
+ }
282
+ if (this.kb.data_visualization_experts) {
283
+ for (const key of Object.keys(this.kb.data_visualization_experts)) {
284
+ if (key.includes(expertName.toLowerCase())) {
285
+ return this.kb.data_visualization_experts[key];
286
+ }
287
+ }
288
+ }
289
+ return null;
290
+ }
291
+ /**
292
+ * Get Duarte's glance test requirements
293
+ */
294
+ getDuarteGlanceTest() {
295
+ this.ensureLoaded();
296
+ const duarte = this.kb.experts.nancy_duarte;
297
+ return {
298
+ wordLimit: duarte.core_principles.glance_test.word_limit || 25,
299
+ timeSeconds: 3
300
+ };
301
+ }
302
+ /**
303
+ * Get Miller's Law constraints
304
+ */
305
+ getMillersLaw() {
306
+ this.ensureLoaded();
307
+ return { minItems: 5, maxItems: 9 };
308
+ }
309
+ /**
310
+ * Get cognitive load constraints
311
+ */
312
+ getCognitiveLoadLimits() {
313
+ this.ensureLoaded();
314
+ return { maxItemsPerChunk: 7 };
315
+ }
316
+ /**
317
+ * Get chart selection guidance
318
+ */
319
+ getChartGuidance(purpose) {
320
+ this.ensureLoaded();
321
+ return this.kb.chart_selection_guide.by_purpose[purpose];
322
+ }
323
+ /**
324
+ * Get charts to avoid
325
+ */
326
+ getChartsToAvoid() {
327
+ this.ensureLoaded();
328
+ const knaflic = this.kb.data_visualization_experts.cole_nussbaumer_knaflic;
329
+ return knaflic.chart_guidance.avoid_these || [];
330
+ }
331
+ /**
332
+ * Get investment banking pitch book structure
333
+ */
334
+ getIBPitchBookStructure(type) {
335
+ this.ensureLoaded();
336
+ return this.kb.investment_banking?.pitch_book_types[type];
337
+ }
338
+ /**
339
+ * Check if knowledge base is loaded
340
+ */
341
+ ensureLoaded() {
342
+ if (!this.loaded) {
343
+ throw new Error("Knowledge base not loaded. Call load() first.");
344
+ }
345
+ }
346
+ /**
347
+ * Get the full knowledge base (for advanced queries)
348
+ */
349
+ getRawKB() {
350
+ this.ensureLoaded();
351
+ return this.kb;
352
+ }
353
+ };
354
+ var gatewayInstance = null;
355
+ async function getKnowledgeGateway() {
356
+ if (!gatewayInstance) {
357
+ gatewayInstance = new KnowledgeGateway();
358
+ await gatewayInstance.load();
359
+ }
360
+ return gatewayInstance;
361
+ }
362
+
91
363
  // src/core/ContentAnalyzer.ts
92
364
  var ContentAnalyzer = class {
365
+ kb;
93
366
  // Signal words for SCQA detection
94
367
  situationSignals = [
95
368
  "currently",
@@ -101,7 +374,8 @@ var ContentAnalyzer = class {
101
374
  "our",
102
375
  "the market",
103
376
  "industry",
104
- "context"
377
+ "context",
378
+ "background"
105
379
  ];
106
380
  complicationSignals = [
107
381
  "however",
@@ -119,20 +393,10 @@ var ContentAnalyzer = class {
119
393
  "yet",
120
394
  "although",
121
395
  "despite",
122
- "while"
123
- ];
124
- questionSignals = [
125
- "how",
126
- "what",
127
- "why",
128
- "when",
129
- "where",
130
- "which",
131
- "should",
132
- "could",
133
- "can we",
134
- "is it possible",
135
- "?"
396
+ "while",
397
+ "crisis",
398
+ "gap",
399
+ "broken"
136
400
  ];
137
401
  answerSignals = [
138
402
  "therefore",
@@ -148,429 +412,439 @@ var ContentAnalyzer = class {
148
412
  "we should",
149
413
  "must",
150
414
  "need to",
151
- "the answer"
415
+ "the answer",
416
+ "introducing",
417
+ "presenting",
418
+ "platform"
152
419
  ];
153
- // Sparkline detection
154
- whatIsSignals = [
155
- "currently",
156
- "today",
157
- "status quo",
158
- "reality",
159
- "actual",
160
- "now",
161
- "existing",
162
- "present state",
163
- "as-is",
164
- "problem"
165
- ];
166
- whatCouldBeSignals = [
167
- "imagine",
168
- "vision",
169
- "future",
170
- "could be",
171
- "what if",
172
- "possibility",
173
- "potential",
174
- "opportunity",
175
- "transform",
176
- "envision",
177
- "ideal",
178
- "dream",
179
- "goal",
180
- "aspiration"
420
+ // CTA signals - used to filter these OUT of answer detection
421
+ ctaSignals = [
422
+ "contact",
423
+ "call us",
424
+ "email",
425
+ "schedule",
426
+ "sign up",
427
+ "register",
428
+ "next steps",
429
+ "get started",
430
+ "reach out",
431
+ "visit",
432
+ "follow"
181
433
  ];
434
+ // Type detection signals
435
+ typeSignals = {
436
+ ted_keynote: ["inspire", "vision", "imagine", "dream", "transform", "story", "journey"],
437
+ sales_pitch: ["buy", "purchase", "pricing", "offer", "deal", "discount", "roi", "value"],
438
+ consulting_deck: ["recommend", "analysis", "assessment", "strategy", "findings", "options", "mece"],
439
+ investment_banking: ["valuation", "dcf", "multiple", "enterprise value", "ebitda", "ipo", "m&a"],
440
+ investor_pitch: ["investment", "funding", "series", "traction", "market size", "tam", "runway"],
441
+ technical_presentation: ["architecture", "api", "system", "implementation", "code", "database", "deploy"],
442
+ all_hands: ["team", "quarter", "update", "progress", "celebrate", "recognize", "company"]
443
+ };
444
+ async initialize() {
445
+ this.kb = await getKnowledgeGateway();
446
+ }
182
447
  /**
183
448
  * Analyze content and extract structural elements.
184
449
  */
185
450
  async analyze(content, contentType) {
451
+ await this.initialize();
186
452
  const text = this.parseContent(content, contentType);
187
- const paragraphs = this.splitIntoParagraphs(text);
188
- const sentences = this.splitIntoSentences(text);
189
- const scqa = this.extractSCQA(paragraphs, sentences);
190
- const sparkline = this.extractSparkline(paragraphs);
191
- const keyMessages = this.extractKeyMessages(text, sentences);
192
- const titles = this.generateActionTitles(keyMessages, paragraphs);
193
- const starMoments = this.identifyStarMoments(paragraphs);
194
- const estimatedSlideCount = this.estimateSlideCount(text, paragraphs);
453
+ const title = this.extractTitle(text);
454
+ const detectedType = this.detectPresentationType(text);
455
+ console.log(` \u2713 Detected type: ${detectedType}`);
456
+ const sections = this.extractSections(text);
457
+ console.log(` \u2713 Found ${sections.length} sections`);
458
+ const scqa = this.extractSCQA(text);
459
+ const sparkline = this.extractSparkline(text);
460
+ const keyMessages = this.extractKeyMessages(text);
461
+ const dataPoints = this.extractDataPoints(text);
462
+ console.log(` \u2713 Found ${dataPoints.length} data points`);
463
+ const titles = sections.map((s) => s.header).filter((h) => h.length > 0);
464
+ const starMoments = this.extractStarMoments(text);
465
+ const estimatedSlideCount = Math.max(5, sections.length + 3);
195
466
  return {
196
- scqa,
197
- sparkline,
467
+ detectedType,
468
+ title,
469
+ sections,
198
470
  keyMessages,
471
+ dataPoints,
472
+ scqa: {
473
+ situation: scqa.situation || "",
474
+ complication: scqa.complication || "",
475
+ question: scqa.question || "",
476
+ answer: scqa.answer || ""
477
+ },
478
+ sparkline: {
479
+ whatIs: sparkline.callToAction ? [sparkline.callToAction] : [],
480
+ whatCouldBe: keyMessages,
481
+ callToAdventure: sparkline.callToAction || ""
482
+ },
199
483
  titles,
200
484
  starMoments,
201
485
  estimatedSlideCount
202
486
  };
203
487
  }
204
488
  /**
205
- * Parse content based on its type.
489
+ * Parse content based on type
206
490
  */
207
491
  parseContent(content, contentType) {
208
- switch (contentType) {
209
- case "markdown":
210
- return this.parseMarkdown(content);
211
- case "json":
212
- return this.parseJSON(content);
213
- case "yaml":
214
- return this.parseYAML(content);
215
- case "text":
216
- default:
217
- return content;
492
+ if (contentType === "markdown" || content.includes("#") || content.includes("- ")) {
493
+ return content;
218
494
  }
495
+ return content;
219
496
  }
220
497
  /**
221
- * Parse markdown content to plain text (preserving structure hints).
222
- */
223
- parseMarkdown(content) {
224
- let text = content;
225
- text = text.replace(/^#{1,6}\s+(.+)$/gm, "\n[HEADER] $1\n");
226
- text = text.replace(/^[-*+]\s+(.+)$/gm, "[BULLET] $1");
227
- text = text.replace(/^\d+\.\s+(.+)$/gm, "[NUMBERED] $1");
228
- text = text.replace(/\*\*(.+?)\*\*/g, "[EMPHASIS] $1 [/EMPHASIS]");
229
- text = text.replace(/\*(.+?)\*/g, "$1");
230
- text = text.replace(/```[\s\S]*?```/g, "[CODE BLOCK]");
231
- text = text.replace(/`(.+?)`/g, "$1");
232
- text = text.replace(/\[(.+?)\]\(.+?\)/g, "$1");
233
- text = text.replace(/!\[.*?\]\(.+?\)/g, "[IMAGE]");
234
- return text.trim();
235
- }
236
- /**
237
- * Parse JSON content.
498
+ * Extract the main title from content
238
499
  */
239
- parseJSON(content) {
240
- try {
241
- const data = JSON.parse(content);
242
- return this.flattenObject(data);
243
- } catch {
244
- return content;
500
+ extractTitle(text) {
501
+ const h1Match = text.match(/^#\s+(.+)$/m);
502
+ if (h1Match && h1Match[1]) {
503
+ return h1Match[1].replace(/\*\*/g, "").trim();
504
+ }
505
+ const lines = text.split("\n").filter((l) => l.trim().length > 0);
506
+ if (lines.length > 0 && lines[0]) {
507
+ return lines[0].replace(/^#+\s*/, "").replace(/\*\*/g, "").trim().slice(0, 80);
245
508
  }
509
+ return "Presentation";
246
510
  }
247
511
  /**
248
- * Parse YAML content.
512
+ * Detect presentation type from content signals
249
513
  */
250
- parseYAML(content) {
251
- const lines = content.split("\n");
252
- const values = [];
253
- for (const line of lines) {
254
- const match = line.match(/^[\s-]*(?:\w+:\s*)?(.+)$/);
255
- if (match?.[1] && !match[1].includes(":")) {
256
- values.push(match[1].trim());
514
+ detectPresentationType(text) {
515
+ const lowerText = text.toLowerCase();
516
+ const scores = {
517
+ ted_keynote: 0,
518
+ sales_pitch: 0,
519
+ consulting_deck: 0,
520
+ investment_banking: 0,
521
+ investor_pitch: 0,
522
+ technical_presentation: 0,
523
+ all_hands: 0
524
+ };
525
+ for (const [type, signals] of Object.entries(this.typeSignals)) {
526
+ for (const signal of signals) {
527
+ if (lowerText.includes(signal)) {
528
+ scores[type]++;
529
+ }
530
+ }
531
+ }
532
+ let maxScore = 0;
533
+ let detectedType = "consulting_deck";
534
+ for (const [type, score] of Object.entries(scores)) {
535
+ if (score > maxScore) {
536
+ maxScore = score;
537
+ detectedType = type;
257
538
  }
258
539
  }
259
- return values.join("\n");
540
+ return detectedType;
260
541
  }
261
542
  /**
262
- * Flatten object to text.
543
+ * Extract sections from content (headers, bullets, content)
263
544
  */
264
- flattenObject(obj, prefix = "") {
265
- const parts = [];
266
- if (typeof obj === "string") {
267
- return obj;
268
- }
269
- if (Array.isArray(obj)) {
270
- for (const item of obj) {
271
- parts.push(this.flattenObject(item, prefix));
545
+ extractSections(text) {
546
+ const sections = [];
547
+ const lines = text.split("\n");
548
+ let currentSection = null;
549
+ let contentLines = [];
550
+ for (const line of lines) {
551
+ const trimmedLine = line.trim();
552
+ const headerMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/);
553
+ if (headerMatch) {
554
+ if (currentSection) {
555
+ currentSection.content = contentLines.join("\n").trim();
556
+ if (currentSection.header || currentSection.content || currentSection.bullets.length > 0) {
557
+ sections.push(currentSection);
558
+ }
559
+ }
560
+ currentSection = {
561
+ header: (headerMatch[2] || "").replace(/\*\*/g, "").trim(),
562
+ level: (headerMatch[1] || "").length,
563
+ content: "",
564
+ bullets: [],
565
+ metrics: []
566
+ };
567
+ contentLines = [];
568
+ continue;
569
+ }
570
+ const bulletMatch = trimmedLine.match(/^[-*+]\s+(.+)$/);
571
+ if (bulletMatch && bulletMatch[1] && currentSection) {
572
+ currentSection.bullets.push(bulletMatch[1]);
573
+ continue;
574
+ }
575
+ const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/);
576
+ if (numberedMatch && numberedMatch[1] && currentSection) {
577
+ currentSection.bullets.push(numberedMatch[1]);
578
+ continue;
579
+ }
580
+ const metricMatch = trimmedLine.match(/\$?([\d,]+\.?\d*)[%]?\s*[-–—:]\s*(.+)/);
581
+ if (metricMatch && metricMatch[1] && metricMatch[2] && currentSection) {
582
+ currentSection.metrics.push({
583
+ value: metricMatch[1],
584
+ label: metricMatch[2].slice(0, 50)
585
+ });
586
+ continue;
587
+ }
588
+ if (trimmedLine.includes("|") && currentSection) {
589
+ const cells = trimmedLine.split("|").map((c) => c.trim()).filter((c) => c && !c.match(/^-+$/));
590
+ if (cells.length >= 2) {
591
+ const value = cells.find((c) => c.match(/^\$?[\d,]+\.?\d*[%]?$/));
592
+ const label = cells.find((c) => !c.match(/^\$?[\d,]+\.?\d*[%]?$/) && c.length > 2);
593
+ if (value && label) {
594
+ currentSection.metrics.push({ value, label: label.slice(0, 50) });
595
+ }
596
+ }
597
+ continue;
272
598
  }
273
- } else if (typeof obj === "object" && obj !== null) {
274
- for (const [key, value] of Object.entries(obj)) {
275
- const newPrefix = prefix ? `${prefix}.${key}` : key;
276
- parts.push(this.flattenObject(value, newPrefix));
599
+ if (trimmedLine && currentSection) {
600
+ contentLines.push(trimmedLine);
277
601
  }
278
- } else if (obj !== null && obj !== void 0) {
279
- parts.push(String(obj));
280
602
  }
281
- return parts.join("\n");
282
- }
283
- /**
284
- * Split text into paragraphs.
285
- */
286
- splitIntoParagraphs(text) {
287
- return text.split(/\n\n+/).map((p) => p.trim()).filter((p) => p.length > 0);
288
- }
289
- /**
290
- * Split text into sentences.
291
- */
292
- splitIntoSentences(text) {
293
- const cleaned = text.replace(/Mr\./g, "Mr").replace(/Mrs\./g, "Mrs").replace(/Dr\./g, "Dr").replace(/vs\./g, "vs").replace(/etc\./g, "etc").replace(/e\.g\./g, "eg").replace(/i\.e\./g, "ie");
294
- return cleaned.split(/[.!?]+/).map((s) => s.trim()).filter((s) => s.length > 10);
603
+ if (currentSection) {
604
+ currentSection.content = contentLines.join("\n").trim();
605
+ if (currentSection.header || currentSection.content || currentSection.bullets.length > 0) {
606
+ sections.push(currentSection);
607
+ }
608
+ }
609
+ return sections;
295
610
  }
296
611
  /**
297
- * Extract SCQA structure (Barbara Minto's Pyramid Principle).
612
+ * Extract SCQA structure (Barbara Minto)
298
613
  */
299
- extractSCQA(paragraphs, sentences) {
614
+ extractSCQA(text) {
615
+ const paragraphs = text.split(/\n\n+/).filter((p) => p.trim());
300
616
  let situation = "";
301
617
  let complication = "";
302
618
  let question = "";
303
619
  let answer = "";
304
620
  for (const para of paragraphs.slice(0, 3)) {
305
- if (this.containsSignals(para, this.situationSignals)) {
306
- situation = this.extractRelevantSentence(para, this.situationSignals);
621
+ if (this.containsSignals(para.toLowerCase(), this.situationSignals)) {
622
+ situation = this.extractFirstSentence(para);
307
623
  break;
308
624
  }
309
625
  }
310
626
  for (const para of paragraphs) {
311
- if (this.containsSignals(para, this.complicationSignals)) {
312
- complication = this.extractRelevantSentence(para, this.complicationSignals);
627
+ if (this.containsSignals(para.toLowerCase(), this.complicationSignals)) {
628
+ complication = this.extractFirstSentence(para);
313
629
  break;
314
630
  }
315
631
  }
316
- for (const sentence of sentences) {
317
- if (sentence.includes("?") || this.containsSignals(sentence, this.questionSignals)) {
318
- question = sentence;
632
+ const middleStart = Math.floor(paragraphs.length * 0.2);
633
+ const middleEnd = Math.floor(paragraphs.length * 0.8);
634
+ for (const para of paragraphs.slice(middleStart, middleEnd)) {
635
+ const lowerPara = para.toLowerCase();
636
+ if (this.containsSignals(lowerPara, this.answerSignals) && !this.containsSignals(lowerPara, this.ctaSignals)) {
637
+ answer = this.extractFirstSentence(para);
319
638
  break;
320
639
  }
321
640
  }
322
- for (const para of paragraphs.slice(-3)) {
323
- if (this.containsSignals(para, this.answerSignals)) {
324
- answer = this.extractRelevantSentence(para, this.answerSignals);
325
- break;
326
- }
641
+ if (!situation && paragraphs.length > 0 && paragraphs[0]) {
642
+ situation = this.extractFirstSentence(paragraphs[0]);
327
643
  }
328
- if (!situation && paragraphs.length > 0) {
329
- situation = this.truncateToSentence(paragraphs[0] ?? "", 150);
644
+ if (!answer && paragraphs.length > 2) {
645
+ for (const para of paragraphs.slice(1, Math.floor(paragraphs.length * 0.5))) {
646
+ const lowerPara = para.toLowerCase();
647
+ if (lowerPara.includes("recommend") || lowerPara.includes("strategy") || lowerPara.includes("solution") || lowerPara.includes("approach")) {
648
+ answer = this.extractFirstSentence(para);
649
+ break;
650
+ }
651
+ }
330
652
  }
331
- if (!answer && paragraphs.length > 1) {
332
- const lastPara = paragraphs[paragraphs.length - 1];
333
- answer = lastPara ? this.truncateToSentence(lastPara, 150) : "";
653
+ if (complication && !question) {
654
+ question = `How do we address: ${complication.slice(0, 80)}?`;
334
655
  }
335
656
  return { situation, complication, question, answer };
336
657
  }
337
658
  /**
338
- * Extract Sparkline structure (Nancy Duarte).
659
+ * Extract STAR moments (Something They'll Always Remember)
660
+ * Per Nancy Duarte - these are emotional peaks in the presentation
339
661
  */
340
- extractSparkline(paragraphs) {
341
- const whatIs = [];
342
- const whatCouldBe = [];
343
- let callToAdventure = "";
344
- for (const para of paragraphs) {
345
- const lowerPara = para.toLowerCase();
346
- if (this.containsSignals(lowerPara, this.whatIsSignals)) {
347
- whatIs.push(this.truncateToSentence(para, 100));
662
+ extractStarMoments(text) {
663
+ const starMoments = [];
664
+ const boldMatches = text.match(/\*\*(.+?)\*\*/g);
665
+ if (boldMatches) {
666
+ for (const match of boldMatches.slice(0, 5)) {
667
+ const cleaned = match.replace(/\*\*/g, "").trim();
668
+ const lowerCleaned = cleaned.toLowerCase();
669
+ if (cleaned.length > 15 && cleaned.length < 100 && !this.containsSignals(lowerCleaned, this.ctaSignals) && !/^\d+[%x]?\s*$/.test(cleaned)) {
670
+ starMoments.push(cleaned);
671
+ }
348
672
  }
349
- if (this.containsSignals(lowerPara, this.whatCouldBeSignals)) {
350
- whatCouldBe.push(this.truncateToSentence(para, 100));
673
+ }
674
+ const exclamations = text.match(/[^.!?]*![^.!?]*/g);
675
+ if (exclamations) {
676
+ for (const ex of exclamations.slice(0, 2)) {
677
+ const cleaned = ex.trim();
678
+ const lowerCleaned = cleaned.toLowerCase();
679
+ if (cleaned.length > 10 && cleaned.length < 100 && !starMoments.includes(cleaned) && !this.containsSignals(lowerCleaned, this.ctaSignals)) {
680
+ starMoments.push(cleaned);
681
+ }
351
682
  }
352
683
  }
353
- for (const para of paragraphs.slice(-2)) {
354
- if (this.containsSignals(para.toLowerCase(), ["join", "together", "action", "start", "begin", "now"])) {
355
- callToAdventure = this.truncateToSentence(para, 150);
684
+ return starMoments.slice(0, 3);
685
+ }
686
+ /**
687
+ * Extract Sparkline elements (Nancy Duarte)
688
+ */
689
+ extractSparkline(text) {
690
+ const paragraphs = text.split(/\n\n+/).filter((p) => p.trim());
691
+ let callToAction = "";
692
+ const ctaSignals = ["next steps", "call to action", "take action", "start now", "join us", "contact"];
693
+ for (const para of paragraphs.slice(-3)) {
694
+ if (this.containsSignals(para.toLowerCase(), ctaSignals)) {
695
+ callToAction = this.extractFirstSentence(para);
356
696
  break;
357
697
  }
358
698
  }
359
- return { whatIs, whatCouldBe, callToAdventure };
699
+ return { callToAction };
360
700
  }
361
701
  /**
362
- * Extract key messages (max 3 - Rule of Three).
702
+ * Extract key messages (max 3 - Rule of Three)
363
703
  */
364
- extractKeyMessages(text, sentences) {
704
+ extractKeyMessages(text) {
365
705
  const messages = [];
366
- const emphasisMatches = text.match(/\[EMPHASIS\](.+?)\[\/EMPHASIS\]/g);
367
- if (emphasisMatches) {
368
- for (const match of emphasisMatches.slice(0, 3)) {
369
- const content = match.replace(/\[EMPHASIS\]|\[\/EMPHASIS\]/g, "").trim();
370
- if (content.length > 10 && content.length < 100) {
371
- messages.push(content);
372
- }
373
- }
374
- }
375
- const headerMatches = text.match(/\[HEADER\](.+)/g);
376
- if (headerMatches && messages.length < 3) {
377
- for (const match of headerMatches.slice(0, 3 - messages.length)) {
378
- const content = match.replace("[HEADER]", "").trim();
379
- if (content.length > 5 && content.length < 80) {
380
- messages.push(content);
706
+ const h2Matches = text.match(/^##\s+(.+)$/gm);
707
+ if (h2Matches) {
708
+ for (const match of h2Matches.slice(0, 3)) {
709
+ const msg = match.replace(/^##\s+/, "").replace(/\*\*/g, "").trim();
710
+ if (msg.length > 5 && msg.length < 80) {
711
+ messages.push(msg);
381
712
  }
382
713
  }
383
714
  }
384
715
  if (messages.length < 3) {
385
- const strongSentences = sentences.filter((s) => s.length > 20 && s.length < 100).filter((s) => this.containsSignals(s.toLowerCase(), ["key", "important", "critical", "essential", "must", "need"]));
386
- for (const sentence of strongSentences.slice(0, 3 - messages.length)) {
387
- messages.push(sentence);
388
- }
389
- }
390
- if (messages.length < 3) {
391
- for (const sentence of sentences) {
392
- if (sentence.length > 30 && sentence.length < 100 && !messages.includes(sentence)) {
393
- messages.push(sentence);
394
- if (messages.length >= 3) break;
716
+ const boldMatches = text.match(/\*\*(.+?)\*\*/g);
717
+ if (boldMatches) {
718
+ for (const match of boldMatches) {
719
+ const msg = match.replace(/\*\*/g, "").trim();
720
+ if (msg.length > 10 && msg.length < 80 && !messages.includes(msg)) {
721
+ messages.push(msg);
722
+ if (messages.length >= 3) break;
723
+ }
395
724
  }
396
725
  }
397
726
  }
398
727
  return messages.slice(0, 3);
399
728
  }
400
729
  /**
401
- * Generate action titles (McKinsey-style).
730
+ * Extract data points (metrics with values)
402
731
  */
403
- generateActionTitles(keyMessages, paragraphs) {
404
- const titles = [];
405
- for (const message of keyMessages) {
406
- const actionTitle = this.transformToActionTitle(message);
407
- if (actionTitle) {
408
- titles.push(actionTitle);
732
+ extractDataPoints(text) {
733
+ const dataPoints = [];
734
+ const dollarMatches = text.match(/\$[\d,]+(?:\.\d+)?[MBK]?(?:\s*(?:million|billion|thousand))?/gi);
735
+ if (dollarMatches) {
736
+ for (const match of dollarMatches.slice(0, 4)) {
737
+ const context = this.getContextAroundMatch(text, match);
738
+ dataPoints.push({
739
+ value: match,
740
+ label: this.extractLabelFromContext(context)
741
+ });
409
742
  }
410
743
  }
411
- for (const para of paragraphs) {
412
- if (titles.length >= 10) break;
413
- const headerMatch = para.match(/\[HEADER\]\s*(.+)/);
414
- if (headerMatch?.[1]) {
415
- const title = this.transformToActionTitle(headerMatch[1]);
416
- if (title && !titles.includes(title)) {
417
- titles.push(title);
744
+ const percentMatches = text.match(/\d+(?:\.\d+)?%/g);
745
+ if (percentMatches) {
746
+ for (const match of percentMatches.slice(0, 4)) {
747
+ if (!dataPoints.some((d) => d.value === match)) {
748
+ const context = this.getContextAroundMatch(text, match);
749
+ dataPoints.push({
750
+ value: match,
751
+ label: this.extractLabelFromContext(context)
752
+ });
418
753
  }
419
754
  }
420
755
  }
421
- return titles;
756
+ return dataPoints.slice(0, 6);
422
757
  }
423
758
  /**
424
- * Transform a statement into an action title.
759
+ * Get context around a match
425
760
  */
426
- transformToActionTitle(statement) {
427
- let title = statement.replace(/\[(HEADER|EMPHASIS|BULLET|NUMBERED)\]/g, "").trim();
428
- const actionVerbs = ["increase", "decrease", "improve", "reduce", "achieve", "deliver", "create", "build", "launch", "transform", "enable", "drive"];
429
- const firstWord = title.split(" ")[0]?.toLowerCase();
430
- if (firstWord && actionVerbs.includes(firstWord)) {
431
- return this.capitalizeFirst(title);
432
- }
433
- if (title.toLowerCase().includes("should")) {
434
- title = title.replace(/we should|you should|should/gi, "").trim();
435
- return this.capitalizeFirst(title);
436
- }
437
- if (title.toLowerCase().includes("need to")) {
438
- title = title.replace(/we need to|you need to|need to/gi, "").trim();
439
- return this.capitalizeFirst(title);
440
- }
441
- if (title.length < 50) {
442
- return title;
443
- }
444
- return this.truncateToWords(title, 8);
761
+ getContextAroundMatch(text, match) {
762
+ const index = text.indexOf(match);
763
+ if (index === -1) return "";
764
+ const start = Math.max(0, index - 50);
765
+ const end = Math.min(text.length, index + match.length + 50);
766
+ return text.slice(start, end);
445
767
  }
446
768
  /**
447
- * Identify STAR moments (Something They'll Always Remember).
769
+ * Extract a label from surrounding context
770
+ * Fixes the "Century Interactive |" garbage issue by stripping table syntax
448
771
  */
449
- identifyStarMoments(paragraphs) {
450
- const starMoments = [];
451
- const starSignals = [
452
- "surprising",
453
- "amazing",
454
- "incredible",
455
- "remarkable",
456
- "stunning",
457
- "imagine",
458
- "what if",
459
- "breakthrough",
460
- "revolutionary",
461
- "never before",
462
- "first time",
463
- "unprecedented",
464
- "game-changing",
465
- "dramatic"
466
- ];
467
- for (const para of paragraphs) {
468
- if (this.containsSignals(para.toLowerCase(), starSignals)) {
469
- const moment = this.truncateToSentence(para, 120);
470
- if (moment.length > 20) {
471
- starMoments.push(moment);
472
- }
473
- }
474
- }
475
- const statPattern = /\d+[%xX]|\$[\d,]+(?:\s*(?:million|billion|trillion))?|\d+(?:\s*(?:million|billion|trillion))/g;
476
- for (const para of paragraphs) {
477
- if (statPattern.test(para) && starMoments.length < 5) {
478
- const moment = this.truncateToSentence(para, 100);
479
- if (!starMoments.includes(moment)) {
480
- starMoments.push(moment);
481
- }
482
- }
483
- }
484
- return starMoments.slice(0, 5);
772
+ extractLabelFromContext(context) {
773
+ let cleaned = context.replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\s{2,}/g, " ").replace(/\*\*/g, "").replace(/#+\s*/g, "").trim();
774
+ const words = cleaned.split(/\s+/).filter((w) => w.length > 2 && !w.match(/^\d/));
775
+ return words.slice(0, 4).join(" ") || "Value";
485
776
  }
486
777
  /**
487
- * Estimate slide count based on content.
778
+ * Check if text contains any of the signals
488
779
  */
489
- estimateSlideCount(text, paragraphs) {
490
- const wordCount = text.split(/\s+/).length;
491
- const headerCount = (text.match(/\[HEADER\]/g) ?? []).length;
492
- const bulletGroups = (text.match(/\[BULLET\]/g) ?? []).length / 4;
493
- const wordBasedEstimate = Math.ceil(wordCount / 35);
494
- const estimate = Math.max(
495
- 5,
496
- // Minimum 5 slides
497
- Math.ceil((wordBasedEstimate + headerCount + bulletGroups) / 2)
498
- );
499
- return Math.min(estimate, 30);
500
- }
501
- // === Helper Methods ===
502
780
  containsSignals(text, signals) {
503
- const lowerText = text.toLowerCase();
504
- return signals.some((signal) => lowerText.includes(signal));
505
- }
506
- extractRelevantSentence(paragraph, signals) {
507
- const sentences = paragraph.split(/[.!?]+/);
508
- for (const sentence of sentences) {
509
- if (this.containsSignals(sentence.toLowerCase(), signals)) {
510
- return sentence.trim();
511
- }
512
- }
513
- return this.truncateToSentence(paragraph, 150);
781
+ return signals.some((signal) => text.includes(signal));
514
782
  }
515
- truncateToSentence(text, maxLength) {
516
- if (text.length <= maxLength) {
517
- return text.trim();
518
- }
519
- const truncated = text.slice(0, maxLength);
520
- const lastPeriod = truncated.lastIndexOf(".");
521
- const lastQuestion = truncated.lastIndexOf("?");
522
- const lastExclaim = truncated.lastIndexOf("!");
523
- const lastBoundary = Math.max(lastPeriod, lastQuestion, lastExclaim);
524
- if (lastBoundary > maxLength * 0.5) {
525
- return text.slice(0, lastBoundary + 1).trim();
526
- }
527
- return truncated.trim() + "...";
528
- }
529
- truncateToWords(text, maxWords) {
530
- const words = text.split(/\s+/);
531
- if (words.length <= maxWords) {
532
- return text;
783
+ /**
784
+ * Extract first meaningful sentence from text
785
+ */
786
+ extractFirstSentence(text) {
787
+ const cleaned = text.replace(/^#+\s*/gm, "").replace(/\*\*/g, "").replace(/\|/g, " ").trim();
788
+ const sentences = cleaned.split(/[.!?]+/);
789
+ const first = sentences[0]?.trim();
790
+ if (first && first.length > 10) {
791
+ return first.slice(0, 150);
533
792
  }
534
- return words.slice(0, maxWords).join(" ");
535
- }
536
- capitalizeFirst(text) {
537
- if (!text) return "";
538
- return text.charAt(0).toUpperCase() + text.slice(1);
793
+ return cleaned.slice(0, 150);
539
794
  }
540
795
  };
541
796
 
542
797
  // src/core/SlideFactory.ts
543
798
  var SlideFactory = class {
544
799
  templates;
800
+ usedContent = /* @__PURE__ */ new Set();
545
801
  constructor() {
546
802
  this.templates = this.initializeTemplates();
547
803
  }
804
+ /**
805
+ * Check if content has already been used (deduplication)
806
+ */
807
+ isContentUsed(content) {
808
+ if (!content) return true;
809
+ const normalized = content.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 50);
810
+ if (this.usedContent.has(normalized)) {
811
+ return true;
812
+ }
813
+ this.usedContent.add(normalized);
814
+ return false;
815
+ }
548
816
  /**
549
817
  * Create slides from analyzed content.
550
818
  */
551
819
  async createSlides(analysis, mode) {
552
820
  const slides = [];
553
821
  let slideIndex = 0;
822
+ this.usedContent.clear();
554
823
  slides.push(this.createTitleSlide(slideIndex++, analysis));
824
+ this.isContentUsed(analysis.titles[0] ?? "");
555
825
  if (mode === "business" && analysis.keyMessages.length >= 2) {
556
826
  slides.push(this.createAgendaSlide(slideIndex++, analysis));
557
827
  }
558
- if (analysis.scqa.situation) {
828
+ if (analysis.scqa.situation && !this.isContentUsed(analysis.scqa.situation)) {
559
829
  slides.push(this.createContextSlide(slideIndex++, analysis, mode));
560
830
  }
561
- if (analysis.scqa.complication) {
831
+ if (analysis.scqa.complication && !this.isContentUsed(analysis.scqa.complication)) {
562
832
  slides.push(this.createProblemSlide(slideIndex++, analysis, mode));
563
833
  }
564
834
  for (const message of analysis.keyMessages) {
565
- slides.push(this.createMessageSlide(slideIndex++, message, mode));
835
+ if (!this.isContentUsed(message)) {
836
+ slides.push(this.createMessageSlide(slideIndex++, message, mode));
837
+ }
566
838
  }
567
839
  for (const starMoment of analysis.starMoments.slice(0, 2)) {
568
- slides.push(this.createStarMomentSlide(slideIndex++, starMoment, mode));
840
+ if (!this.isContentUsed(starMoment)) {
841
+ slides.push(this.createStarMomentSlide(slideIndex++, starMoment, mode));
842
+ }
569
843
  }
570
- if (analysis.scqa.answer) {
844
+ if (analysis.scqa.answer && !this.isContentUsed(analysis.scqa.answer)) {
571
845
  slides.push(this.createSolutionSlide(slideIndex++, analysis, mode));
572
846
  }
573
- if (analysis.sparkline.callToAdventure) {
847
+ if (analysis.sparkline.callToAdventure && !this.isContentUsed(analysis.sparkline.callToAdventure)) {
574
848
  slides.push(this.createCTASlide(slideIndex++, analysis, mode));
575
849
  }
576
850
  slides.push(this.createThankYouSlide(slideIndex++));
@@ -580,13 +854,18 @@ var SlideFactory = class {
580
854
  * Create a title slide.
581
855
  */
582
856
  createTitleSlide(index, analysis) {
583
- const subtitle = analysis.keyMessages[0] ?? analysis.scqa.answer ?? "";
857
+ let subtitle = "";
858
+ if (analysis.scqa.answer && analysis.scqa.answer.length > 10) {
859
+ subtitle = analysis.scqa.answer;
860
+ } else if (analysis.starMoments.length > 0 && analysis.starMoments[0]) {
861
+ subtitle = analysis.starMoments[0];
862
+ }
584
863
  return {
585
864
  index,
586
865
  type: "title",
587
866
  data: {
588
867
  title: analysis.titles[0] ?? "Presentation",
589
- subtitle: this.truncate(subtitle, 60),
868
+ subtitle: this.truncate(subtitle, 80),
590
869
  keyMessage: analysis.scqa.answer
591
870
  },
592
871
  classes: ["slide-title"]
@@ -1817,19 +2096,28 @@ var ScoreCalculator = class {
1817
2096
  var import_playwright = require("playwright");
1818
2097
  var QAEngine = class {
1819
2098
  browser = null;
2099
+ kb;
2100
+ /**
2101
+ * Initialize KnowledgeGateway
2102
+ */
2103
+ async initialize() {
2104
+ this.kb = await getKnowledgeGateway();
2105
+ }
1820
2106
  /**
1821
- * Validate a presentation.
2107
+ * Validate a presentation using KB-driven quality metrics
1822
2108
  */
1823
2109
  async validate(presentation, options) {
2110
+ await this.initialize();
1824
2111
  const html = typeof presentation === "string" ? presentation : presentation.toString("utf-8");
1825
2112
  const mode = options?.mode ?? "keynote";
2113
+ const qualityMetrics = this.kb.getQualityMetrics();
1826
2114
  await this.initBrowser();
1827
2115
  try {
1828
2116
  const [visualResults, contentResults, expertResults, accessibilityResults] = await Promise.all([
1829
- this.runVisualTests(html, mode),
1830
- this.runContentTests(html, mode),
2117
+ this.runVisualTests(html, mode, qualityMetrics),
2118
+ this.runContentTests(html, mode, qualityMetrics),
1831
2119
  this.runExpertTests(html, mode),
1832
- this.runAccessibilityTests(html)
2120
+ this.runAccessibilityTests(html, qualityMetrics)
1833
2121
  ]);
1834
2122
  const issues = this.collectIssues(visualResults, contentResults, expertResults, accessibilityResults);
1835
2123
  const errorCount = issues.filter((i) => i.severity === "error").length;
@@ -1847,18 +2135,18 @@ var QAEngine = class {
1847
2135
  }
1848
2136
  }
1849
2137
  /**
1850
- * Calculate overall QA score.
2138
+ * Calculate overall QA score using KB-driven weights
1851
2139
  */
1852
2140
  calculateScore(results) {
1853
2141
  const weights = {
1854
2142
  visual: 0.35,
1855
- // 35%
2143
+ // 35% - Design quality
1856
2144
  content: 0.3,
1857
- // 30%
2145
+ // 30% - Content quality
1858
2146
  expert: 0.25,
1859
- // 25%
2147
+ // 25% - Expert methodology
1860
2148
  accessibility: 0.1
1861
- // 10%
2149
+ // 10% - Accessibility
1862
2150
  };
1863
2151
  const visualScore = this.calculateVisualScore(results.visual);
1864
2152
  const contentScore = this.calculateContentScore(results.content);
@@ -1905,13 +2193,15 @@ var QAEngine = class {
1905
2193
  };
1906
2194
  }
1907
2195
  // ===========================================================================
1908
- // VISUAL TESTS
2196
+ // VISUAL TESTS - KB-DRIVEN
1909
2197
  // ===========================================================================
1910
- async runVisualTests(html, mode) {
2198
+ async runVisualTests(html, mode, qualityMetrics) {
1911
2199
  const page = await this.browser.newPage();
1912
2200
  await page.setViewportSize({ width: 1280, height: 720 });
1913
2201
  await page.setContent(html);
1914
2202
  await page.waitForTimeout(1e3);
2203
+ const modeMetrics = mode === "keynote" ? qualityMetrics.keynote_mode : qualityMetrics.business_mode;
2204
+ const minWhitespace = this.kb.getWhitespaceRequirement(mode) * 100;
1915
2205
  const slideCount = await page.evaluate(() => {
1916
2206
  return window.Reveal?.getTotalSlides?.() ?? document.querySelectorAll(".slides > section").length;
1917
2207
  });
@@ -1977,9 +2267,8 @@ var QAEngine = class {
1977
2267
  }, { slideIndex: i });
1978
2268
  if (slideAnalysis) {
1979
2269
  const issues = [];
1980
- const minWhitespace = mode === "keynote" ? 40 : 25;
1981
2270
  if (slideAnalysis.whitespace < minWhitespace) {
1982
- issues.push(`Whitespace ${slideAnalysis.whitespace}% below ${minWhitespace}% minimum`);
2271
+ issues.push(`Whitespace ${slideAnalysis.whitespace}% below ${minWhitespace}% minimum (KB-defined)`);
1983
2272
  }
1984
2273
  if (slideAnalysis.whitespace > 80) {
1985
2274
  issues.push(`Whitespace ${slideAnalysis.whitespace}% - slide appears sparse`);
@@ -2034,34 +2323,35 @@ var QAEngine = class {
2034
2323
  };
2035
2324
  }
2036
2325
  // ===========================================================================
2037
- // CONTENT TESTS
2326
+ // CONTENT TESTS - KB-DRIVEN
2038
2327
  // ===========================================================================
2039
- async runContentTests(html, mode) {
2328
+ async runContentTests(html, mode, qualityMetrics) {
2040
2329
  const page = await this.browser.newPage();
2041
2330
  await page.setContent(html);
2042
2331
  await page.waitForTimeout(500);
2043
- const results = await page.evaluate((targetMode) => {
2332
+ const wordLimits = this.kb.getWordLimits(mode);
2333
+ const glanceTest = this.kb.getDuarteGlanceTest();
2334
+ const results = await page.evaluate((params) => {
2335
+ const { targetMode, maxWords, minWords, glanceWordLimit } = params;
2044
2336
  const slides = document.querySelectorAll(".slides > section");
2045
2337
  const perSlide = [];
2046
- const glanceTest = [];
2338
+ const glanceTestResults = [];
2047
2339
  const signalNoise = [];
2048
2340
  const oneIdea = [];
2049
2341
  slides.forEach((slide, index) => {
2050
2342
  const text = slide.innerText || "";
2051
2343
  const words = text.split(/\s+/).filter((w) => w.length > 0);
2052
2344
  const wordCount = words.length;
2053
- const maxWords = targetMode === "keynote" ? 25 : 80;
2054
- const minWords = targetMode === "business" ? 20 : 0;
2055
2345
  const withinLimit = wordCount <= maxWords && wordCount >= minWords;
2056
2346
  const title = slide.querySelector("h2")?.textContent || "";
2057
2347
  const hasVerb = /\b(is|are|was|were|has|have|had|will|can|could|should|would|may|might|must|exceeded|increased|decreased|grew|fell|drove|caused|enabled|prevented|achieved|failed|creates?|generates?|delivers?|provides?|shows?|demonstrates?)\b/i.test(title);
2058
2348
  const hasInsight = title.length > 30 && hasVerb;
2059
2349
  const issues = [];
2060
2350
  if (!withinLimit) {
2061
- issues.push(`Word count ${wordCount} outside ${minWords}-${maxWords} range`);
2351
+ issues.push(`Word count ${wordCount} outside ${minWords}-${maxWords} range (KB-defined)`);
2062
2352
  }
2063
2353
  if (targetMode === "business" && !hasInsight && index > 0) {
2064
- issues.push("Title is not action-oriented (missing insight)");
2354
+ issues.push("Title is not action-oriented (missing insight per Minto)");
2065
2355
  }
2066
2356
  perSlide.push({
2067
2357
  slideIndex: index,
@@ -2074,12 +2364,12 @@ var QAEngine = class {
2074
2364
  const keyMessage = prominentElement?.textContent?.trim() || "";
2075
2365
  const keyWordCount = keyMessage.split(/\s+/).filter((w) => w.length > 0).length;
2076
2366
  const readingTime = keyWordCount / 4.2;
2077
- glanceTest.push({
2367
+ glanceTestResults.push({
2078
2368
  slideIndex: index,
2079
2369
  keyMessage,
2080
2370
  wordCount: keyWordCount,
2081
2371
  readingTime: Math.round(readingTime * 10) / 10,
2082
- passed: readingTime <= 3 && keyWordCount <= 15,
2372
+ passed: readingTime <= 3 && keyWordCount <= glanceWordLimit,
2083
2373
  recommendation: readingTime > 3 ? `Shorten to ${Math.floor(3 * 4.2)} words or less` : void 0
2084
2374
  });
2085
2375
  const elements = slide.querySelectorAll("h1, h2, h3, p, li, img");
@@ -2106,35 +2396,143 @@ var QAEngine = class {
2106
2396
  conflictingIdeas: ideaCount > 2 ? ["Multiple competing ideas detected"] : void 0
2107
2397
  });
2108
2398
  });
2109
- return { perSlide, glanceTest, signalNoise, oneIdea };
2110
- }, mode);
2399
+ return { perSlide, glanceTest: glanceTestResults, signalNoise, oneIdea };
2400
+ }, {
2401
+ targetMode: mode,
2402
+ maxWords: wordLimits.max,
2403
+ minWords: mode === "business" ? 20 : 0,
2404
+ glanceWordLimit: glanceTest.wordLimit
2405
+ });
2111
2406
  await page.close();
2112
2407
  return results;
2113
2408
  }
2114
2409
  // ===========================================================================
2115
- // EXPERT TESTS
2410
+ // EXPERT TESTS - KB-DRIVEN
2116
2411
  // ===========================================================================
2117
2412
  async runExpertTests(html, mode) {
2413
+ const duartePrinciples = this.kb.getExpertPrinciples("nancy_duarte");
2414
+ const reynoldsPrinciples = this.kb.getExpertPrinciples("garr_reynolds");
2415
+ const galloPrinciples = this.kb.getExpertPrinciples("carmine_gallo");
2416
+ const andersonPrinciples = this.kb.getExpertPrinciples("chris_anderson");
2118
2417
  return {
2119
- duarte: this.createExpertResult("Nancy Duarte", ["Glance Test", "STAR Moment", "Sparkline"], 85),
2120
- reynolds: this.createExpertResult("Garr Reynolds", ["Signal-to-Noise", "Simplicity", "Picture Superiority"], 80),
2121
- gallo: this.createExpertResult("Carmine Gallo", ["Rule of Three", "Emotional Connection"], 85),
2122
- anderson: this.createExpertResult("Chris Anderson", ["One Idea", "Clarity"], 90)
2418
+ duarte: this.validateDuartePrinciples(html, duartePrinciples),
2419
+ reynolds: this.validateReynoldsPrinciples(html, reynoldsPrinciples),
2420
+ gallo: this.validateGalloPrinciples(html, galloPrinciples),
2421
+ anderson: this.validateAndersonPrinciples(html, andersonPrinciples)
2123
2422
  };
2124
2423
  }
2125
- createExpertResult(name, principles, score) {
2424
+ validateDuartePrinciples(html, principles) {
2425
+ const violations = [];
2426
+ let score = 100;
2427
+ const slideMatches = html.match(/<section[^>]*>[\s\S]*?<\/section>/gi) || [];
2428
+ slideMatches.forEach((slide, index) => {
2429
+ const h1Match = slide.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
2430
+ const h2Match = slide.match(/<h2[^>]*>([\s\S]*?)<\/h2>/i);
2431
+ const keyMessage = (h1Match?.[1] || h2Match?.[1] || "").replace(/<[^>]+>/g, "").trim();
2432
+ const wordCount = keyMessage.split(/\s+/).filter((w) => w.length > 0).length;
2433
+ if (wordCount > 25) {
2434
+ violations.push(`Slide ${index + 1}: Key message exceeds 25 words (Duarte Glance Test)`);
2435
+ score -= 5;
2436
+ }
2437
+ });
2438
+ const hasStarMoment = html.includes("callout") || html.includes("highlight") || html.includes("big-idea");
2439
+ if (!hasStarMoment && slideMatches.length > 5) {
2440
+ violations.push("Missing STAR moment (Something They'll Always Remember)");
2441
+ score -= 10;
2442
+ }
2126
2443
  return {
2127
- expertName: name,
2128
- principlesChecked: principles,
2444
+ expertName: "Nancy Duarte",
2445
+ principlesChecked: ["Glance Test", "STAR Moment", "Sparkline Structure"],
2129
2446
  passed: score >= 80,
2130
- score,
2131
- violations: score < 80 ? [`${name} principles not fully met`] : []
2447
+ score: Math.max(0, score),
2448
+ violations
2449
+ };
2450
+ }
2451
+ validateReynoldsPrinciples(html, principles) {
2452
+ const violations = [];
2453
+ let score = 100;
2454
+ const decorativeElements = (html.match(/class="[^"]*decorative[^"]*"/gi) || []).length;
2455
+ const randomImages = (html.match(/picsum\.photos/gi) || []).length;
2456
+ if (randomImages > 0) {
2457
+ violations.push(`Found ${randomImages} random stock images (Reynolds: only use purposeful images)`);
2458
+ score -= randomImages * 10;
2459
+ }
2460
+ if (decorativeElements > 3) {
2461
+ violations.push(`Too many decorative elements (${decorativeElements}) - reduces signal-to-noise ratio`);
2462
+ score -= 10;
2463
+ }
2464
+ const columnLayouts = (html.match(/class="[^"]*columns[^"]*"/gi) || []).length;
2465
+ const slideCount = (html.match(/<section/gi) || []).length;
2466
+ if (columnLayouts > slideCount * 0.5) {
2467
+ violations.push("Overuse of column layouts - simplify per Reynolds");
2468
+ score -= 10;
2469
+ }
2470
+ return {
2471
+ expertName: "Garr Reynolds",
2472
+ principlesChecked: ["Signal-to-Noise", "Simplicity", "Picture Superiority"],
2473
+ passed: score >= 80,
2474
+ score: Math.max(0, score),
2475
+ violations
2476
+ };
2477
+ }
2478
+ validateGalloPrinciples(html, principles) {
2479
+ const violations = [];
2480
+ let score = 100;
2481
+ const bulletLists = html.match(/<ul[^>]*>[\s\S]*?<\/ul>/gi) || [];
2482
+ bulletLists.forEach((list, index) => {
2483
+ const bullets = (list.match(/<li/gi) || []).length;
2484
+ if (bullets > 5) {
2485
+ violations.push(`Bullet list ${index + 1} has ${bullets} items (Gallo: max 3-5)`);
2486
+ score -= 5;
2487
+ }
2488
+ });
2489
+ const hasQuote = html.includes("blockquote") || html.includes("quote");
2490
+ const hasStory = html.toLowerCase().includes("story") || html.toLowerCase().includes("journey");
2491
+ if (!hasQuote && !hasStory) {
2492
+ violations.push("Missing emotional connection elements (quotes, stories)");
2493
+ score -= 10;
2494
+ }
2495
+ return {
2496
+ expertName: "Carmine Gallo",
2497
+ principlesChecked: ["Rule of Three", "Emotional Connection", "Headline Power"],
2498
+ passed: score >= 80,
2499
+ score: Math.max(0, score),
2500
+ violations
2501
+ };
2502
+ }
2503
+ validateAndersonPrinciples(html, principles) {
2504
+ const violations = [];
2505
+ let score = 100;
2506
+ const slideMatches = html.match(/<section[^>]*>[\s\S]*?<\/section>/gi) || [];
2507
+ slideMatches.forEach((slide, index) => {
2508
+ const headings = (slide.match(/<h[12][^>]*>/gi) || []).length;
2509
+ if (headings > 2) {
2510
+ violations.push(`Slide ${index + 1}: Multiple ideas detected (Anderson: one idea per slide)`);
2511
+ score -= 5;
2512
+ }
2513
+ });
2514
+ const jargonPatterns = ["synergy", "leverage", "paradigm", "holistic", "streamline"];
2515
+ let jargonCount = 0;
2516
+ jargonPatterns.forEach((pattern) => {
2517
+ const matches = html.toLowerCase().match(new RegExp(pattern, "gi")) || [];
2518
+ jargonCount += matches.length;
2519
+ });
2520
+ if (jargonCount > 5) {
2521
+ violations.push(`Excessive jargon detected (${jargonCount} instances) - simplify language`);
2522
+ score -= 10;
2523
+ }
2524
+ return {
2525
+ expertName: "Chris Anderson",
2526
+ principlesChecked: ["One Idea", "Clarity", "Throughline"],
2527
+ passed: score >= 80,
2528
+ score: Math.max(0, score),
2529
+ violations
2132
2530
  };
2133
2531
  }
2134
2532
  // ===========================================================================
2135
- // ACCESSIBILITY TESTS
2533
+ // ACCESSIBILITY TESTS - KB-DRIVEN
2136
2534
  // ===========================================================================
2137
- async runAccessibilityTests(html) {
2535
+ async runAccessibilityTests(html, qualityMetrics) {
2138
2536
  const page = await this.browser.newPage();
2139
2537
  await page.setContent(html);
2140
2538
  await page.waitForTimeout(500);
@@ -2184,7 +2582,7 @@ var QAEngine = class {
2184
2582
  };
2185
2583
  }
2186
2584
  // ===========================================================================
2187
- // SCORING
2585
+ // SCORING - KB-DRIVEN
2188
2586
  // ===========================================================================
2189
2587
  calculateVisualScore(results) {
2190
2588
  let score = 100;
@@ -2308,6 +2706,7 @@ var QAEngine = class {
2308
2706
  // src/generators/html/RevealJsGenerator.ts
2309
2707
  var RevealJsGenerator = class {
2310
2708
  templateEngine;
2709
+ kb;
2311
2710
  defaultRevealConfig = {
2312
2711
  revealVersion: "5.0.4",
2313
2712
  hash: true,
@@ -2326,20 +2725,32 @@ var RevealJsGenerator = class {
2326
2725
  constructor() {
2327
2726
  this.templateEngine = new TemplateEngine();
2328
2727
  }
2728
+ /**
2729
+ * Initialize KnowledgeGateway
2730
+ */
2731
+ async initialize() {
2732
+ this.kb = await getKnowledgeGateway();
2733
+ }
2329
2734
  /**
2330
2735
  * Generate complete Reveal.js HTML presentation.
2331
2736
  */
2332
2737
  async generate(slides, config) {
2738
+ await this.initialize();
2333
2739
  const templateConfig = {};
2334
2740
  if (config.theme) templateConfig.theme = config.theme;
2335
2741
  if (config.customTemplates) templateConfig.customTemplates = config.customTemplates;
2336
2742
  const slideHtml = this.templateEngine.renderAll(slides, templateConfig);
2743
+ const presentationType = this.detectPresentationType(config);
2744
+ const palette = this.kb.getRecommendedPalette(presentationType);
2745
+ const typography = this.kb.getTypography(config.mode);
2337
2746
  const docConfig = {
2338
2747
  title: config.title,
2339
2748
  slides: slideHtml.join("\n"),
2340
2749
  theme: config.theme ?? "default",
2341
2750
  revealConfig: this.defaultRevealConfig,
2342
- mode: config.mode
2751
+ mode: config.mode,
2752
+ palette,
2753
+ typography
2343
2754
  };
2344
2755
  if (config.author) docConfig.author = config.author;
2345
2756
  if (config.subject) docConfig.subject = config.subject;
@@ -2350,11 +2761,23 @@ var RevealJsGenerator = class {
2350
2761
  }
2351
2762
  return html;
2352
2763
  }
2764
+ /**
2765
+ * Detect presentation type from config
2766
+ */
2767
+ detectPresentationType(config) {
2768
+ if (config.presentationType) {
2769
+ return config.presentationType;
2770
+ }
2771
+ if (config.mode === "keynote") {
2772
+ return "ted_keynote";
2773
+ }
2774
+ return "consulting_deck";
2775
+ }
2353
2776
  /**
2354
2777
  * Build the complete HTML document.
2355
2778
  */
2356
2779
  buildDocument(options) {
2357
- const { title, author, subject, slides, theme, customCSS, revealConfig, mode } = options;
2780
+ const { title, author, subject, slides, theme, customCSS, revealConfig, mode, palette, typography } = options;
2358
2781
  return `<!DOCTYPE html>
2359
2782
  <html lang="en">
2360
2783
  <head>
@@ -2362,7 +2785,7 @@ var RevealJsGenerator = class {
2362
2785
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
2363
2786
  <meta name="author" content="${this.escapeHtml(author ?? "Claude Presentation Master")}">
2364
2787
  <meta name="description" content="${this.escapeHtml(subject ?? "")}">
2365
- <meta name="generator" content="Claude Presentation Master v1.0.0">
2788
+ <meta name="generator" content="Claude Presentation Master v6.0.0">
2366
2789
  <title>${this.escapeHtml(title)}</title>
2367
2790
 
2368
2791
  <!-- Reveal.js CSS -->
@@ -2376,10 +2799,10 @@ var RevealJsGenerator = class {
2376
2799
  <!-- Mermaid for diagrams -->
2377
2800
  <script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
2378
2801
 
2379
- <!-- Presentation Engine CSS -->
2802
+ <!-- Presentation Engine CSS - KB-Driven Design System -->
2380
2803
  <style>
2381
- ${this.getBaseStyles(mode)}
2382
- ${this.getThemeStyles(theme)}
2804
+ ${this.getBaseStyles(mode, palette, typography)}
2805
+ ${this.getThemeStyles(theme, palette)}
2383
2806
  ${this.getAnimationStyles()}
2384
2807
  ${customCSS ?? ""}
2385
2808
  </style>
@@ -2431,26 +2854,35 @@ ${slides}
2431
2854
  </html>`;
2432
2855
  }
2433
2856
  /**
2434
- * Get base styles for slides.
2857
+ * Get base styles for slides - KB-DRIVEN
2435
2858
  */
2436
- getBaseStyles(mode) {
2859
+ getBaseStyles(mode, palette, typography) {
2437
2860
  const fontSize = mode === "keynote" ? "2.5em" : "1.8em";
2438
2861
  const lineHeight = mode === "keynote" ? "1.4" : "1.5";
2862
+ const primary = palette.primary || "#1a1a2e";
2863
+ const secondary = palette.secondary || "#16213e";
2864
+ const accent = palette.accent || "#0f3460";
2865
+ const highlight = palette.accent || "#e94560";
2866
+ const text = palette.text || "#1a1a2e";
2867
+ const background = palette.background || "#ffffff";
2439
2868
  return `
2440
- /* Base Styles */
2869
+ /* Base Styles - KB-Driven Design System */
2441
2870
  :root {
2871
+ /* Typography from KB */
2442
2872
  --font-heading: 'Source Sans Pro', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, sans-serif;
2443
2873
  --font-body: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2444
2874
  --font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
2445
2875
 
2446
- --color-primary: #1a1a2e;
2447
- --color-secondary: #16213e;
2448
- --color-accent: #0f3460;
2449
- --color-highlight: #e94560;
2450
- --color-text: #1a1a2e;
2451
- --color-text-light: #4a4a68;
2452
- --color-background: #ffffff;
2876
+ /* Colors from KB Palette: ${palette.name} */
2877
+ --color-primary: ${primary};
2878
+ --color-secondary: ${secondary};
2879
+ --color-accent: ${accent};
2880
+ --color-highlight: ${highlight};
2881
+ --color-text: ${text};
2882
+ --color-text-light: ${this.lightenColor(text, 30)};
2883
+ --color-background: ${background};
2453
2884
 
2885
+ /* Layout */
2454
2886
  --slide-padding: 60px;
2455
2887
  --content-max-width: 1200px;
2456
2888
  }
@@ -2566,7 +2998,7 @@ ${slides}
2566
2998
  font-size: 0.8em;
2567
2999
  }
2568
3000
 
2569
- /* Images */
3001
+ /* Images - NO RANDOM PICSUM - Only user-provided or none */
2570
3002
  .reveal img {
2571
3003
  max-width: 100%;
2572
3004
  height: auto;
@@ -2672,9 +3104,25 @@ ${slides}
2672
3104
  `;
2673
3105
  }
2674
3106
  /**
2675
- * Get theme-specific styles.
3107
+ * Lighten a hex color
3108
+ */
3109
+ lightenColor(hex, percent) {
3110
+ hex = hex.replace("#", "");
3111
+ let r = parseInt(hex.substring(0, 2), 16);
3112
+ let g = parseInt(hex.substring(2, 4), 16);
3113
+ let b = parseInt(hex.substring(4, 6), 16);
3114
+ r = Math.min(255, Math.floor(r + (255 - r) * (percent / 100)));
3115
+ g = Math.min(255, Math.floor(g + (255 - g) * (percent / 100)));
3116
+ b = Math.min(255, Math.floor(b + (255 - b) * (percent / 100)));
3117
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
3118
+ }
3119
+ /**
3120
+ * Get theme-specific styles - KB-DRIVEN
2676
3121
  */
2677
- getThemeStyles(theme) {
3122
+ getThemeStyles(theme, palette) {
3123
+ if (theme === "default") {
3124
+ return "";
3125
+ }
2678
3126
  const themes = {
2679
3127
  "default": "",
2680
3128
  "light-corporate": `
@@ -2688,10 +3136,10 @@ ${slides}
2688
3136
  `,
2689
3137
  "modern-tech": `
2690
3138
  :root {
2691
- --color-primary: #1a1a2e;
2692
- --color-secondary: #16213e;
2693
- --color-accent: #0f3460;
2694
- --color-highlight: #e94560;
3139
+ --color-primary: ${palette.primary || "#1a1a2e"};
3140
+ --color-secondary: ${palette.secondary || "#16213e"};
3141
+ --color-accent: ${palette.accent || "#0f3460"};
3142
+ --color-highlight: ${palette.accent || "#e94560"};
2695
3143
  --color-background: #f8f9fa;
2696
3144
  }
2697
3145
  `,
@@ -3937,240 +4385,6 @@ function createDefaultImageProvider(options) {
3937
4385
  return new CompositeImageProvider(providers);
3938
4386
  }
3939
4387
 
3940
- // src/knowledge/KnowledgeBase.ts
3941
- var import_fs = require("fs");
3942
- var import_path = require("path");
3943
- var import_url = require("url");
3944
- var yaml = __toESM(require("yaml"));
3945
- var import_meta = {};
3946
- function getModuleDir() {
3947
- if (typeof import_meta !== "undefined" && import_meta.url) {
3948
- return (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
3949
- }
3950
- if (typeof __dirname !== "undefined") {
3951
- return __dirname;
3952
- }
3953
- return process.cwd();
3954
- }
3955
- var moduleDir = getModuleDir();
3956
- var KnowledgeBase = class {
3957
- data = null;
3958
- loaded = false;
3959
- /**
3960
- * Load the knowledge base from the bundled YAML file.
3961
- */
3962
- async load() {
3963
- if (this.loaded) return;
3964
- try {
3965
- const possiblePaths = [
3966
- // From dist/: go up to package root
3967
- (0, import_path.join)(moduleDir, "../../assets/presentation-knowledge.yaml"),
3968
- (0, import_path.join)(moduleDir, "../assets/presentation-knowledge.yaml"),
3969
- (0, import_path.join)(moduleDir, "../../../assets/presentation-knowledge.yaml"),
3970
- // From bundle in dist/index.js: go up one level
3971
- (0, import_path.join)(moduleDir, "assets/presentation-knowledge.yaml"),
3972
- // CWD-based paths for development or local installs
3973
- (0, import_path.join)(process.cwd(), "assets/presentation-knowledge.yaml"),
3974
- (0, import_path.join)(process.cwd(), "node_modules/claude-presentation-master/assets/presentation-knowledge.yaml")
3975
- ];
3976
- let assetPath = "";
3977
- for (const p of possiblePaths) {
3978
- try {
3979
- (0, import_fs.readFileSync)(p);
3980
- assetPath = p;
3981
- break;
3982
- } catch {
3983
- continue;
3984
- }
3985
- }
3986
- if (!assetPath) {
3987
- throw new Error("Could not locate knowledge base file");
3988
- }
3989
- const content = (0, import_fs.readFileSync)(assetPath, "utf-8");
3990
- this.data = yaml.parse(content);
3991
- this.loaded = true;
3992
- console.log(`\u{1F4DA} Knowledge base loaded: v${this.data.version}`);
3993
- } catch (error) {
3994
- console.warn("\u26A0\uFE0F Could not load knowledge base, using defaults");
3995
- this.data = this.getDefaultData();
3996
- this.loaded = true;
3997
- }
3998
- }
3999
- /**
4000
- * Get expert methodology by name.
4001
- */
4002
- getExpert(name) {
4003
- this.ensureLoaded();
4004
- return this.data?.experts?.[name];
4005
- }
4006
- /**
4007
- * Get all expert names.
4008
- */
4009
- getExpertNames() {
4010
- this.ensureLoaded();
4011
- return Object.keys(this.data?.experts ?? {});
4012
- }
4013
- /**
4014
- * Get framework recommendation for audience.
4015
- */
4016
- getFrameworkForAudience(audience) {
4017
- this.ensureLoaded();
4018
- return this.data?.frameworkSelector?.byAudience?.[audience];
4019
- }
4020
- /**
4021
- * Get framework recommendation for goal.
4022
- */
4023
- getFrameworkForGoal(goal) {
4024
- this.ensureLoaded();
4025
- return this.data?.frameworkSelector?.byGoal?.[goal];
4026
- }
4027
- /**
4028
- * Get QA scoring rubric.
4029
- */
4030
- getScoringRubric() {
4031
- this.ensureLoaded();
4032
- return this.data?.automatedQA?.scoringRubric;
4033
- }
4034
- /**
4035
- * Get mode configuration (keynote or business).
4036
- */
4037
- getModeConfig(mode) {
4038
- this.ensureLoaded();
4039
- return this.data?.modes?.[mode];
4040
- }
4041
- /**
4042
- * Get slide type configuration.
4043
- */
4044
- getSlideType(type) {
4045
- this.ensureLoaded();
4046
- return this.data?.slideTypes?.[type];
4047
- }
4048
- /**
4049
- * Get the knowledge base version.
4050
- */
4051
- getVersion() {
4052
- this.ensureLoaded();
4053
- return this.data?.version ?? "unknown";
4054
- }
4055
- /**
4056
- * Validate a slide against expert principles.
4057
- */
4058
- validateAgainstExpert(expertName, slideData) {
4059
- const expert = this.getExpert(expertName);
4060
- if (!expert) {
4061
- return { passed: true, violations: [] };
4062
- }
4063
- const violations = [];
4064
- if (expert.wordLimits) {
4065
- if (expert.wordLimits.max && slideData.wordCount > expert.wordLimits.max) {
4066
- violations.push(`Exceeds ${expertName} word limit of ${expert.wordLimits.max}`);
4067
- }
4068
- if (expert.wordLimits.min && slideData.wordCount < expert.wordLimits.min) {
4069
- violations.push(`Below ${expertName} minimum of ${expert.wordLimits.min} words`);
4070
- }
4071
- }
4072
- return {
4073
- passed: violations.length === 0,
4074
- violations
4075
- };
4076
- }
4077
- /**
4078
- * Ensure knowledge base is loaded.
4079
- */
4080
- ensureLoaded() {
4081
- if (!this.loaded) {
4082
- this.data = this.getDefaultData();
4083
- this.loaded = true;
4084
- }
4085
- }
4086
- /**
4087
- * Get default data if YAML can't be loaded.
4088
- */
4089
- getDefaultData() {
4090
- return {
4091
- version: "1.0.0-fallback",
4092
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
4093
- experts: {
4094
- "Nancy Duarte": {
4095
- name: "Nancy Duarte",
4096
- principles: [
4097
- { name: "Glance Test", description: "Message clear in 3 seconds" },
4098
- { name: "STAR Moment", description: "Something They'll Always Remember" },
4099
- { name: "Sparkline", description: "Contrast What Is vs What Could Be" }
4100
- ]
4101
- },
4102
- "Garr Reynolds": {
4103
- name: "Garr Reynolds",
4104
- principles: [
4105
- { name: "Signal-to-Noise", description: "Maximize signal, minimize noise" },
4106
- { name: "Simplicity", description: "Amplify through simplification" }
4107
- ]
4108
- },
4109
- "Carmine Gallo": {
4110
- name: "Carmine Gallo",
4111
- principles: [
4112
- { name: "Rule of Three", description: "Maximum 3 key messages" },
4113
- { name: "Emotional Connection", description: "Connect emotionally first" }
4114
- ]
4115
- },
4116
- "Chris Anderson": {
4117
- name: "Chris Anderson",
4118
- principles: [
4119
- { name: "One Idea", description: "One powerful idea per talk" },
4120
- { name: "Dead Laptop Test", description: "Present without slides" }
4121
- ]
4122
- }
4123
- },
4124
- frameworkSelector: {
4125
- byAudience: {
4126
- "board": {
4127
- primaryFramework: "Barbara Minto",
4128
- slideTypes: ["executive_summary", "data_insight"]
4129
- },
4130
- "sales": {
4131
- primaryFramework: "Nancy Duarte",
4132
- slideTypes: ["big_idea", "social_proof"]
4133
- }
4134
- },
4135
- byGoal: {
4136
- "persuade": {
4137
- primaryFramework: "Nancy Duarte",
4138
- slideTypes: ["big_idea", "star_moment"]
4139
- },
4140
- "inform": {
4141
- primaryFramework: "Barbara Minto",
4142
- slideTypes: ["bullet_points", "data_insight"]
4143
- }
4144
- }
4145
- },
4146
- automatedQA: {
4147
- scoringRubric: {
4148
- totalPoints: 100,
4149
- passingThreshold: 95,
4150
- categories: {
4151
- visual: { weight: 35, checks: {} },
4152
- content: { weight: 30, checks: {} },
4153
- expert: { weight: 25, checks: {} },
4154
- accessibility: { weight: 10, checks: {} }
4155
- }
4156
- }
4157
- },
4158
- slideTypes: {},
4159
- modes: {
4160
- keynote: { maxWords: 25, minWhitespace: 35 },
4161
- business: { maxWords: 80, minWhitespace: 25 }
4162
- }
4163
- };
4164
- }
4165
- };
4166
- var knowledgeBaseInstance = null;
4167
- function getKnowledgeBase() {
4168
- if (!knowledgeBaseInstance) {
4169
- knowledgeBaseInstance = new KnowledgeBase();
4170
- }
4171
- return knowledgeBaseInstance;
4172
- }
4173
-
4174
4388
  // src/index.ts
4175
4389
  async function generate(config) {
4176
4390
  const engine = new PresentationEngine();
@@ -4185,7 +4399,7 @@ async function validate(presentation, options) {
4185
4399
  score
4186
4400
  };
4187
4401
  }
4188
- var VERSION = "1.0.0";
4402
+ var VERSION = "6.0.0";
4189
4403
  var index_default = {
4190
4404
  generate,
4191
4405
  validate,
@@ -4199,7 +4413,7 @@ var index_default = {
4199
4413
  CompositeChartProvider,
4200
4414
  CompositeImageProvider,
4201
4415
  ContentAnalyzer,
4202
- KnowledgeBase,
4416
+ KnowledgeGateway,
4203
4417
  LocalImageProvider,
4204
4418
  MermaidProvider,
4205
4419
  PlaceholderImageProvider,
@@ -4219,7 +4433,7 @@ var index_default = {
4219
4433
  createDefaultChartProvider,
4220
4434
  createDefaultImageProvider,
4221
4435
  generate,
4222
- getKnowledgeBase,
4436
+ getKnowledgeGateway,
4223
4437
  validate
4224
4438
  });
4225
4439
  /**