claude-presentation-master 4.4.1 → 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/bin/cli.js +2 -2
- package/dist/index.d.mts +311 -158
- package/dist/index.d.ts +311 -158
- package/dist/index.js +846 -622
- package/dist/index.mjs +843 -619
- package/package.json +12 -20
package/dist/index.mjs
CHANGED
|
@@ -26,8 +26,280 @@ var TemplateNotFoundError = class extends Error {
|
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
+
// src/kb/KnowledgeGateway.ts
|
|
30
|
+
import { readFileSync } from "fs";
|
|
31
|
+
import { join, dirname } from "path";
|
|
32
|
+
import { fileURLToPath } from "url";
|
|
33
|
+
import * as yaml from "yaml";
|
|
34
|
+
function getModuleDir() {
|
|
35
|
+
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
36
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
}
|
|
38
|
+
if (typeof __dirname !== "undefined") {
|
|
39
|
+
return __dirname;
|
|
40
|
+
}
|
|
41
|
+
return process.cwd();
|
|
42
|
+
}
|
|
43
|
+
var KnowledgeGateway = class {
|
|
44
|
+
kb;
|
|
45
|
+
loaded = false;
|
|
46
|
+
constructor() {
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load the knowledge base from YAML file
|
|
50
|
+
*/
|
|
51
|
+
async load() {
|
|
52
|
+
if (this.loaded) return;
|
|
53
|
+
const moduleDir = getModuleDir();
|
|
54
|
+
const possiblePaths = [
|
|
55
|
+
join(moduleDir, "../../assets/presentation-knowledge.yaml"),
|
|
56
|
+
join(moduleDir, "../assets/presentation-knowledge.yaml"),
|
|
57
|
+
join(moduleDir, "../../../assets/presentation-knowledge.yaml"),
|
|
58
|
+
join(moduleDir, "assets/presentation-knowledge.yaml"),
|
|
59
|
+
join(process.cwd(), "assets/presentation-knowledge.yaml"),
|
|
60
|
+
join(process.cwd(), "node_modules/claude-presentation-master/assets/presentation-knowledge.yaml")
|
|
61
|
+
];
|
|
62
|
+
for (const path of possiblePaths) {
|
|
63
|
+
try {
|
|
64
|
+
const content = readFileSync(path, "utf-8");
|
|
65
|
+
this.kb = yaml.parse(content);
|
|
66
|
+
this.loaded = true;
|
|
67
|
+
console.log(` \u2713 Knowledge base loaded from ${path}`);
|
|
68
|
+
return;
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
throw new Error(`Knowledge base not found. Tried: ${possiblePaths.join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get presentation mode configuration (keynote or business)
|
|
76
|
+
*/
|
|
77
|
+
getMode(mode) {
|
|
78
|
+
this.ensureLoaded();
|
|
79
|
+
return this.kb.presentation_modes[mode];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Determine which mode is best for a given presentation type
|
|
83
|
+
*/
|
|
84
|
+
getModeForType(type) {
|
|
85
|
+
const keynoteTypes = ["ted_keynote", "sales_pitch", "all_hands"];
|
|
86
|
+
const businessTypes = ["consulting_deck", "investment_banking", "investor_pitch", "technical_presentation"];
|
|
87
|
+
if (keynoteTypes.includes(type)) return "keynote";
|
|
88
|
+
if (businessTypes.includes(type)) return "business";
|
|
89
|
+
return "keynote";
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get word limits for the specified mode
|
|
93
|
+
*/
|
|
94
|
+
getWordLimits(mode) {
|
|
95
|
+
const modeConfig = this.getMode(mode);
|
|
96
|
+
const range = modeConfig.characteristics.words_per_slide;
|
|
97
|
+
if (mode === "keynote") {
|
|
98
|
+
return { min: 6, max: 15, ideal: 10 };
|
|
99
|
+
} else {
|
|
100
|
+
return { min: 40, max: 80, ideal: 60 };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get bullet point limits
|
|
105
|
+
*/
|
|
106
|
+
getBulletLimits(mode) {
|
|
107
|
+
if (mode === "keynote") {
|
|
108
|
+
return { maxBullets: 3, maxWordsPerBullet: 8 };
|
|
109
|
+
} else {
|
|
110
|
+
return { maxBullets: 5, maxWordsPerBullet: 20 };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get whitespace percentage requirement
|
|
115
|
+
*/
|
|
116
|
+
getWhitespaceRequirement(mode) {
|
|
117
|
+
if (mode === "keynote") return 0.4;
|
|
118
|
+
return 0.25;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get slide templates for the specified mode
|
|
122
|
+
*/
|
|
123
|
+
getSlideTemplates(mode) {
|
|
124
|
+
this.ensureLoaded();
|
|
125
|
+
const key = mode === "keynote" ? "keynote_mode" : "business_mode";
|
|
126
|
+
return this.kb.slide_templates[key] || [];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a specific slide template by name
|
|
130
|
+
*/
|
|
131
|
+
getSlideTemplate(mode, templateName) {
|
|
132
|
+
const templates = this.getSlideTemplates(mode);
|
|
133
|
+
return templates.find((t) => t.name.toLowerCase().includes(templateName.toLowerCase()));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get quality metrics for grading
|
|
137
|
+
*/
|
|
138
|
+
getQualityMetrics() {
|
|
139
|
+
this.ensureLoaded();
|
|
140
|
+
return this.kb.quality_metrics;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get anti-patterns to avoid
|
|
144
|
+
*/
|
|
145
|
+
getAntiPatterns(mode) {
|
|
146
|
+
this.ensureLoaded();
|
|
147
|
+
const key = mode === "keynote" ? "keynote_mode" : "business_mode";
|
|
148
|
+
return this.kb.anti_patterns[key] || [];
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get data visualization anti-patterns
|
|
152
|
+
*/
|
|
153
|
+
getDataVizAntiPatterns() {
|
|
154
|
+
this.ensureLoaded();
|
|
155
|
+
return this.kb.anti_patterns.data_visualization || [];
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get accessibility anti-patterns
|
|
159
|
+
*/
|
|
160
|
+
getAccessibilityAntiPatterns() {
|
|
161
|
+
this.ensureLoaded();
|
|
162
|
+
return this.kb.anti_patterns.accessibility || [];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get color palette by name
|
|
166
|
+
*/
|
|
167
|
+
getColorPalette(name) {
|
|
168
|
+
this.ensureLoaded();
|
|
169
|
+
const palettes = this.kb.design_system.color_palettes;
|
|
170
|
+
return palettes[name];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get recommended color palette for presentation type
|
|
174
|
+
*/
|
|
175
|
+
getRecommendedPalette(type) {
|
|
176
|
+
this.ensureLoaded();
|
|
177
|
+
const palettes = this.kb.design_system.color_palettes;
|
|
178
|
+
switch (type) {
|
|
179
|
+
case "consulting_deck":
|
|
180
|
+
return palettes.consulting_classic;
|
|
181
|
+
case "investment_banking":
|
|
182
|
+
case "investor_pitch":
|
|
183
|
+
return palettes.executive_professional;
|
|
184
|
+
case "ted_keynote":
|
|
185
|
+
return palettes.dark_executive;
|
|
186
|
+
case "sales_pitch":
|
|
187
|
+
return palettes.modern_business;
|
|
188
|
+
case "technical_presentation":
|
|
189
|
+
return palettes.modern_business;
|
|
190
|
+
case "all_hands":
|
|
191
|
+
return palettes.strategy_growth;
|
|
192
|
+
default:
|
|
193
|
+
return palettes.consulting_classic;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get typography guidelines for mode
|
|
198
|
+
*/
|
|
199
|
+
getTypography(mode) {
|
|
200
|
+
this.ensureLoaded();
|
|
201
|
+
const key = mode === "keynote" ? "keynote_mode" : "business_mode";
|
|
202
|
+
return this.kb.design_system.typography[key];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get story framework by name
|
|
206
|
+
*/
|
|
207
|
+
getStoryFramework(name) {
|
|
208
|
+
this.ensureLoaded();
|
|
209
|
+
return this.kb.story_frameworks[name];
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get expert principles by name
|
|
213
|
+
*/
|
|
214
|
+
getExpertPrinciples(expertName) {
|
|
215
|
+
this.ensureLoaded();
|
|
216
|
+
if (this.kb.experts[expertName]) {
|
|
217
|
+
return this.kb.experts[expertName];
|
|
218
|
+
}
|
|
219
|
+
if (this.kb.data_visualization_experts) {
|
|
220
|
+
for (const key of Object.keys(this.kb.data_visualization_experts)) {
|
|
221
|
+
if (key.includes(expertName.toLowerCase())) {
|
|
222
|
+
return this.kb.data_visualization_experts[key];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get Duarte's glance test requirements
|
|
230
|
+
*/
|
|
231
|
+
getDuarteGlanceTest() {
|
|
232
|
+
this.ensureLoaded();
|
|
233
|
+
const duarte = this.kb.experts.nancy_duarte;
|
|
234
|
+
return {
|
|
235
|
+
wordLimit: duarte.core_principles.glance_test.word_limit || 25,
|
|
236
|
+
timeSeconds: 3
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get Miller's Law constraints
|
|
241
|
+
*/
|
|
242
|
+
getMillersLaw() {
|
|
243
|
+
this.ensureLoaded();
|
|
244
|
+
return { minItems: 5, maxItems: 9 };
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get cognitive load constraints
|
|
248
|
+
*/
|
|
249
|
+
getCognitiveLoadLimits() {
|
|
250
|
+
this.ensureLoaded();
|
|
251
|
+
return { maxItemsPerChunk: 7 };
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get chart selection guidance
|
|
255
|
+
*/
|
|
256
|
+
getChartGuidance(purpose) {
|
|
257
|
+
this.ensureLoaded();
|
|
258
|
+
return this.kb.chart_selection_guide.by_purpose[purpose];
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get charts to avoid
|
|
262
|
+
*/
|
|
263
|
+
getChartsToAvoid() {
|
|
264
|
+
this.ensureLoaded();
|
|
265
|
+
const knaflic = this.kb.data_visualization_experts.cole_nussbaumer_knaflic;
|
|
266
|
+
return knaflic.chart_guidance.avoid_these || [];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Get investment banking pitch book structure
|
|
270
|
+
*/
|
|
271
|
+
getIBPitchBookStructure(type) {
|
|
272
|
+
this.ensureLoaded();
|
|
273
|
+
return this.kb.investment_banking?.pitch_book_types[type];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Check if knowledge base is loaded
|
|
277
|
+
*/
|
|
278
|
+
ensureLoaded() {
|
|
279
|
+
if (!this.loaded) {
|
|
280
|
+
throw new Error("Knowledge base not loaded. Call load() first.");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get the full knowledge base (for advanced queries)
|
|
285
|
+
*/
|
|
286
|
+
getRawKB() {
|
|
287
|
+
this.ensureLoaded();
|
|
288
|
+
return this.kb;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
var gatewayInstance = null;
|
|
292
|
+
async function getKnowledgeGateway() {
|
|
293
|
+
if (!gatewayInstance) {
|
|
294
|
+
gatewayInstance = new KnowledgeGateway();
|
|
295
|
+
await gatewayInstance.load();
|
|
296
|
+
}
|
|
297
|
+
return gatewayInstance;
|
|
298
|
+
}
|
|
299
|
+
|
|
29
300
|
// src/core/ContentAnalyzer.ts
|
|
30
301
|
var ContentAnalyzer = class {
|
|
302
|
+
kb;
|
|
31
303
|
// Signal words for SCQA detection
|
|
32
304
|
situationSignals = [
|
|
33
305
|
"currently",
|
|
@@ -39,7 +311,8 @@ var ContentAnalyzer = class {
|
|
|
39
311
|
"our",
|
|
40
312
|
"the market",
|
|
41
313
|
"industry",
|
|
42
|
-
"context"
|
|
314
|
+
"context",
|
|
315
|
+
"background"
|
|
43
316
|
];
|
|
44
317
|
complicationSignals = [
|
|
45
318
|
"however",
|
|
@@ -57,20 +330,10 @@ var ContentAnalyzer = class {
|
|
|
57
330
|
"yet",
|
|
58
331
|
"although",
|
|
59
332
|
"despite",
|
|
60
|
-
"while"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
"what",
|
|
65
|
-
"why",
|
|
66
|
-
"when",
|
|
67
|
-
"where",
|
|
68
|
-
"which",
|
|
69
|
-
"should",
|
|
70
|
-
"could",
|
|
71
|
-
"can we",
|
|
72
|
-
"is it possible",
|
|
73
|
-
"?"
|
|
333
|
+
"while",
|
|
334
|
+
"crisis",
|
|
335
|
+
"gap",
|
|
336
|
+
"broken"
|
|
74
337
|
];
|
|
75
338
|
answerSignals = [
|
|
76
339
|
"therefore",
|
|
@@ -86,429 +349,439 @@ var ContentAnalyzer = class {
|
|
|
86
349
|
"we should",
|
|
87
350
|
"must",
|
|
88
351
|
"need to",
|
|
89
|
-
"the answer"
|
|
352
|
+
"the answer",
|
|
353
|
+
"introducing",
|
|
354
|
+
"presenting",
|
|
355
|
+
"platform"
|
|
90
356
|
];
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
whatCouldBeSignals = [
|
|
105
|
-
"imagine",
|
|
106
|
-
"vision",
|
|
107
|
-
"future",
|
|
108
|
-
"could be",
|
|
109
|
-
"what if",
|
|
110
|
-
"possibility",
|
|
111
|
-
"potential",
|
|
112
|
-
"opportunity",
|
|
113
|
-
"transform",
|
|
114
|
-
"envision",
|
|
115
|
-
"ideal",
|
|
116
|
-
"dream",
|
|
117
|
-
"goal",
|
|
118
|
-
"aspiration"
|
|
357
|
+
// CTA signals - used to filter these OUT of answer detection
|
|
358
|
+
ctaSignals = [
|
|
359
|
+
"contact",
|
|
360
|
+
"call us",
|
|
361
|
+
"email",
|
|
362
|
+
"schedule",
|
|
363
|
+
"sign up",
|
|
364
|
+
"register",
|
|
365
|
+
"next steps",
|
|
366
|
+
"get started",
|
|
367
|
+
"reach out",
|
|
368
|
+
"visit",
|
|
369
|
+
"follow"
|
|
119
370
|
];
|
|
371
|
+
// Type detection signals
|
|
372
|
+
typeSignals = {
|
|
373
|
+
ted_keynote: ["inspire", "vision", "imagine", "dream", "transform", "story", "journey"],
|
|
374
|
+
sales_pitch: ["buy", "purchase", "pricing", "offer", "deal", "discount", "roi", "value"],
|
|
375
|
+
consulting_deck: ["recommend", "analysis", "assessment", "strategy", "findings", "options", "mece"],
|
|
376
|
+
investment_banking: ["valuation", "dcf", "multiple", "enterprise value", "ebitda", "ipo", "m&a"],
|
|
377
|
+
investor_pitch: ["investment", "funding", "series", "traction", "market size", "tam", "runway"],
|
|
378
|
+
technical_presentation: ["architecture", "api", "system", "implementation", "code", "database", "deploy"],
|
|
379
|
+
all_hands: ["team", "quarter", "update", "progress", "celebrate", "recognize", "company"]
|
|
380
|
+
};
|
|
381
|
+
async initialize() {
|
|
382
|
+
this.kb = await getKnowledgeGateway();
|
|
383
|
+
}
|
|
120
384
|
/**
|
|
121
385
|
* Analyze content and extract structural elements.
|
|
122
386
|
*/
|
|
123
387
|
async analyze(content, contentType) {
|
|
388
|
+
await this.initialize();
|
|
124
389
|
const text = this.parseContent(content, contentType);
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
390
|
+
const title = this.extractTitle(text);
|
|
391
|
+
const detectedType = this.detectPresentationType(text);
|
|
392
|
+
console.log(` \u2713 Detected type: ${detectedType}`);
|
|
393
|
+
const sections = this.extractSections(text);
|
|
394
|
+
console.log(` \u2713 Found ${sections.length} sections`);
|
|
395
|
+
const scqa = this.extractSCQA(text);
|
|
396
|
+
const sparkline = this.extractSparkline(text);
|
|
397
|
+
const keyMessages = this.extractKeyMessages(text);
|
|
398
|
+
const dataPoints = this.extractDataPoints(text);
|
|
399
|
+
console.log(` \u2713 Found ${dataPoints.length} data points`);
|
|
400
|
+
const titles = sections.map((s) => s.header).filter((h) => h.length > 0);
|
|
401
|
+
const starMoments = this.extractStarMoments(text);
|
|
402
|
+
const estimatedSlideCount = Math.max(5, sections.length + 3);
|
|
133
403
|
return {
|
|
134
|
-
|
|
135
|
-
|
|
404
|
+
detectedType,
|
|
405
|
+
title,
|
|
406
|
+
sections,
|
|
136
407
|
keyMessages,
|
|
408
|
+
dataPoints,
|
|
409
|
+
scqa: {
|
|
410
|
+
situation: scqa.situation || "",
|
|
411
|
+
complication: scqa.complication || "",
|
|
412
|
+
question: scqa.question || "",
|
|
413
|
+
answer: scqa.answer || ""
|
|
414
|
+
},
|
|
415
|
+
sparkline: {
|
|
416
|
+
whatIs: sparkline.callToAction ? [sparkline.callToAction] : [],
|
|
417
|
+
whatCouldBe: keyMessages,
|
|
418
|
+
callToAdventure: sparkline.callToAction || ""
|
|
419
|
+
},
|
|
137
420
|
titles,
|
|
138
421
|
starMoments,
|
|
139
422
|
estimatedSlideCount
|
|
140
423
|
};
|
|
141
424
|
}
|
|
142
425
|
/**
|
|
143
|
-
* Parse content based on
|
|
426
|
+
* Parse content based on type
|
|
144
427
|
*/
|
|
145
428
|
parseContent(content, contentType) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return this.parseMarkdown(content);
|
|
149
|
-
case "json":
|
|
150
|
-
return this.parseJSON(content);
|
|
151
|
-
case "yaml":
|
|
152
|
-
return this.parseYAML(content);
|
|
153
|
-
case "text":
|
|
154
|
-
default:
|
|
155
|
-
return content;
|
|
429
|
+
if (contentType === "markdown" || content.includes("#") || content.includes("- ")) {
|
|
430
|
+
return content;
|
|
156
431
|
}
|
|
432
|
+
return content;
|
|
157
433
|
}
|
|
158
434
|
/**
|
|
159
|
-
*
|
|
160
|
-
*/
|
|
161
|
-
parseMarkdown(content) {
|
|
162
|
-
let text = content;
|
|
163
|
-
text = text.replace(/^#{1,6}\s+(.+)$/gm, "\n[HEADER] $1\n");
|
|
164
|
-
text = text.replace(/^[-*+]\s+(.+)$/gm, "[BULLET] $1");
|
|
165
|
-
text = text.replace(/^\d+\.\s+(.+)$/gm, "[NUMBERED] $1");
|
|
166
|
-
text = text.replace(/\*\*(.+?)\*\*/g, "[EMPHASIS] $1 [/EMPHASIS]");
|
|
167
|
-
text = text.replace(/\*(.+?)\*/g, "$1");
|
|
168
|
-
text = text.replace(/```[\s\S]*?```/g, "[CODE BLOCK]");
|
|
169
|
-
text = text.replace(/`(.+?)`/g, "$1");
|
|
170
|
-
text = text.replace(/\[(.+?)\]\(.+?\)/g, "$1");
|
|
171
|
-
text = text.replace(/!\[.*?\]\(.+?\)/g, "[IMAGE]");
|
|
172
|
-
return text.trim();
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Parse JSON content.
|
|
435
|
+
* Extract the main title from content
|
|
176
436
|
*/
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return
|
|
181
|
-
}
|
|
182
|
-
|
|
437
|
+
extractTitle(text) {
|
|
438
|
+
const h1Match = text.match(/^#\s+(.+)$/m);
|
|
439
|
+
if (h1Match && h1Match[1]) {
|
|
440
|
+
return h1Match[1].replace(/\*\*/g, "").trim();
|
|
441
|
+
}
|
|
442
|
+
const lines = text.split("\n").filter((l) => l.trim().length > 0);
|
|
443
|
+
if (lines.length > 0 && lines[0]) {
|
|
444
|
+
return lines[0].replace(/^#+\s*/, "").replace(/\*\*/g, "").trim().slice(0, 80);
|
|
183
445
|
}
|
|
446
|
+
return "Presentation";
|
|
184
447
|
}
|
|
185
448
|
/**
|
|
186
|
-
*
|
|
449
|
+
* Detect presentation type from content signals
|
|
187
450
|
*/
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
451
|
+
detectPresentationType(text) {
|
|
452
|
+
const lowerText = text.toLowerCase();
|
|
453
|
+
const scores = {
|
|
454
|
+
ted_keynote: 0,
|
|
455
|
+
sales_pitch: 0,
|
|
456
|
+
consulting_deck: 0,
|
|
457
|
+
investment_banking: 0,
|
|
458
|
+
investor_pitch: 0,
|
|
459
|
+
technical_presentation: 0,
|
|
460
|
+
all_hands: 0
|
|
461
|
+
};
|
|
462
|
+
for (const [type, signals] of Object.entries(this.typeSignals)) {
|
|
463
|
+
for (const signal of signals) {
|
|
464
|
+
if (lowerText.includes(signal)) {
|
|
465
|
+
scores[type]++;
|
|
466
|
+
}
|
|
195
467
|
}
|
|
196
468
|
}
|
|
197
|
-
|
|
469
|
+
let maxScore = 0;
|
|
470
|
+
let detectedType = "consulting_deck";
|
|
471
|
+
for (const [type, score] of Object.entries(scores)) {
|
|
472
|
+
if (score > maxScore) {
|
|
473
|
+
maxScore = score;
|
|
474
|
+
detectedType = type;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return detectedType;
|
|
198
478
|
}
|
|
199
479
|
/**
|
|
200
|
-
*
|
|
480
|
+
* Extract sections from content (headers, bullets, content)
|
|
201
481
|
*/
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
482
|
+
extractSections(text) {
|
|
483
|
+
const sections = [];
|
|
484
|
+
const lines = text.split("\n");
|
|
485
|
+
let currentSection = null;
|
|
486
|
+
let contentLines = [];
|
|
487
|
+
for (const line of lines) {
|
|
488
|
+
const trimmedLine = line.trim();
|
|
489
|
+
const headerMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/);
|
|
490
|
+
if (headerMatch) {
|
|
491
|
+
if (currentSection) {
|
|
492
|
+
currentSection.content = contentLines.join("\n").trim();
|
|
493
|
+
if (currentSection.header || currentSection.content || currentSection.bullets.length > 0) {
|
|
494
|
+
sections.push(currentSection);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
currentSection = {
|
|
498
|
+
header: (headerMatch[2] || "").replace(/\*\*/g, "").trim(),
|
|
499
|
+
level: (headerMatch[1] || "").length,
|
|
500
|
+
content: "",
|
|
501
|
+
bullets: [],
|
|
502
|
+
metrics: []
|
|
503
|
+
};
|
|
504
|
+
contentLines = [];
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const bulletMatch = trimmedLine.match(/^[-*+]\s+(.+)$/);
|
|
508
|
+
if (bulletMatch && bulletMatch[1] && currentSection) {
|
|
509
|
+
currentSection.bullets.push(bulletMatch[1]);
|
|
510
|
+
continue;
|
|
210
511
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
512
|
+
const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/);
|
|
513
|
+
if (numberedMatch && numberedMatch[1] && currentSection) {
|
|
514
|
+
currentSection.bullets.push(numberedMatch[1]);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const metricMatch = trimmedLine.match(/\$?([\d,]+\.?\d*)[%]?\s*[-–—:]\s*(.+)/);
|
|
518
|
+
if (metricMatch && metricMatch[1] && metricMatch[2] && currentSection) {
|
|
519
|
+
currentSection.metrics.push({
|
|
520
|
+
value: metricMatch[1],
|
|
521
|
+
label: metricMatch[2].slice(0, 50)
|
|
522
|
+
});
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (trimmedLine.includes("|") && currentSection) {
|
|
526
|
+
const cells = trimmedLine.split("|").map((c) => c.trim()).filter((c) => c && !c.match(/^-+$/));
|
|
527
|
+
if (cells.length >= 2) {
|
|
528
|
+
const value = cells.find((c) => c.match(/^\$?[\d,]+\.?\d*[%]?$/));
|
|
529
|
+
const label = cells.find((c) => !c.match(/^\$?[\d,]+\.?\d*[%]?$/) && c.length > 2);
|
|
530
|
+
if (value && label) {
|
|
531
|
+
currentSection.metrics.push({ value, label: label.slice(0, 50) });
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
if (trimmedLine && currentSection) {
|
|
537
|
+
contentLines.push(trimmedLine);
|
|
215
538
|
}
|
|
216
|
-
} else if (obj !== null && obj !== void 0) {
|
|
217
|
-
parts.push(String(obj));
|
|
218
539
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Split text into sentences.
|
|
229
|
-
*/
|
|
230
|
-
splitIntoSentences(text) {
|
|
231
|
-
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");
|
|
232
|
-
return cleaned.split(/[.!?]+/).map((s) => s.trim()).filter((s) => s.length > 10);
|
|
540
|
+
if (currentSection) {
|
|
541
|
+
currentSection.content = contentLines.join("\n").trim();
|
|
542
|
+
if (currentSection.header || currentSection.content || currentSection.bullets.length > 0) {
|
|
543
|
+
sections.push(currentSection);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return sections;
|
|
233
547
|
}
|
|
234
548
|
/**
|
|
235
|
-
* Extract SCQA structure (Barbara Minto
|
|
549
|
+
* Extract SCQA structure (Barbara Minto)
|
|
236
550
|
*/
|
|
237
|
-
extractSCQA(
|
|
551
|
+
extractSCQA(text) {
|
|
552
|
+
const paragraphs = text.split(/\n\n+/).filter((p) => p.trim());
|
|
238
553
|
let situation = "";
|
|
239
554
|
let complication = "";
|
|
240
555
|
let question = "";
|
|
241
556
|
let answer = "";
|
|
242
557
|
for (const para of paragraphs.slice(0, 3)) {
|
|
243
|
-
if (this.containsSignals(para, this.situationSignals)) {
|
|
244
|
-
situation = this.
|
|
558
|
+
if (this.containsSignals(para.toLowerCase(), this.situationSignals)) {
|
|
559
|
+
situation = this.extractFirstSentence(para);
|
|
245
560
|
break;
|
|
246
561
|
}
|
|
247
562
|
}
|
|
248
563
|
for (const para of paragraphs) {
|
|
249
|
-
if (this.containsSignals(para, this.complicationSignals)) {
|
|
250
|
-
complication = this.
|
|
564
|
+
if (this.containsSignals(para.toLowerCase(), this.complicationSignals)) {
|
|
565
|
+
complication = this.extractFirstSentence(para);
|
|
251
566
|
break;
|
|
252
567
|
}
|
|
253
568
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
569
|
+
const middleStart = Math.floor(paragraphs.length * 0.2);
|
|
570
|
+
const middleEnd = Math.floor(paragraphs.length * 0.8);
|
|
571
|
+
for (const para of paragraphs.slice(middleStart, middleEnd)) {
|
|
572
|
+
const lowerPara = para.toLowerCase();
|
|
573
|
+
if (this.containsSignals(lowerPara, this.answerSignals) && !this.containsSignals(lowerPara, this.ctaSignals)) {
|
|
574
|
+
answer = this.extractFirstSentence(para);
|
|
257
575
|
break;
|
|
258
576
|
}
|
|
259
577
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
answer = this.extractRelevantSentence(para, this.answerSignals);
|
|
263
|
-
break;
|
|
264
|
-
}
|
|
578
|
+
if (!situation && paragraphs.length > 0 && paragraphs[0]) {
|
|
579
|
+
situation = this.extractFirstSentence(paragraphs[0]);
|
|
265
580
|
}
|
|
266
|
-
if (!
|
|
267
|
-
|
|
581
|
+
if (!answer && paragraphs.length > 2) {
|
|
582
|
+
for (const para of paragraphs.slice(1, Math.floor(paragraphs.length * 0.5))) {
|
|
583
|
+
const lowerPara = para.toLowerCase();
|
|
584
|
+
if (lowerPara.includes("recommend") || lowerPara.includes("strategy") || lowerPara.includes("solution") || lowerPara.includes("approach")) {
|
|
585
|
+
answer = this.extractFirstSentence(para);
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
268
589
|
}
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
answer = lastPara ? this.truncateToSentence(lastPara, 150) : "";
|
|
590
|
+
if (complication && !question) {
|
|
591
|
+
question = `How do we address: ${complication.slice(0, 80)}?`;
|
|
272
592
|
}
|
|
273
593
|
return { situation, complication, question, answer };
|
|
274
594
|
}
|
|
275
595
|
/**
|
|
276
|
-
* Extract
|
|
596
|
+
* Extract STAR moments (Something They'll Always Remember)
|
|
597
|
+
* Per Nancy Duarte - these are emotional peaks in the presentation
|
|
277
598
|
*/
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
599
|
+
extractStarMoments(text) {
|
|
600
|
+
const starMoments = [];
|
|
601
|
+
const boldMatches = text.match(/\*\*(.+?)\*\*/g);
|
|
602
|
+
if (boldMatches) {
|
|
603
|
+
for (const match of boldMatches.slice(0, 5)) {
|
|
604
|
+
const cleaned = match.replace(/\*\*/g, "").trim();
|
|
605
|
+
const lowerCleaned = cleaned.toLowerCase();
|
|
606
|
+
if (cleaned.length > 15 && cleaned.length < 100 && !this.containsSignals(lowerCleaned, this.ctaSignals) && !/^\d+[%x]?\s*$/.test(cleaned)) {
|
|
607
|
+
starMoments.push(cleaned);
|
|
608
|
+
}
|
|
286
609
|
}
|
|
287
|
-
|
|
288
|
-
|
|
610
|
+
}
|
|
611
|
+
const exclamations = text.match(/[^.!?]*![^.!?]*/g);
|
|
612
|
+
if (exclamations) {
|
|
613
|
+
for (const ex of exclamations.slice(0, 2)) {
|
|
614
|
+
const cleaned = ex.trim();
|
|
615
|
+
const lowerCleaned = cleaned.toLowerCase();
|
|
616
|
+
if (cleaned.length > 10 && cleaned.length < 100 && !starMoments.includes(cleaned) && !this.containsSignals(lowerCleaned, this.ctaSignals)) {
|
|
617
|
+
starMoments.push(cleaned);
|
|
618
|
+
}
|
|
289
619
|
}
|
|
290
620
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
621
|
+
return starMoments.slice(0, 3);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Extract Sparkline elements (Nancy Duarte)
|
|
625
|
+
*/
|
|
626
|
+
extractSparkline(text) {
|
|
627
|
+
const paragraphs = text.split(/\n\n+/).filter((p) => p.trim());
|
|
628
|
+
let callToAction = "";
|
|
629
|
+
const ctaSignals = ["next steps", "call to action", "take action", "start now", "join us", "contact"];
|
|
630
|
+
for (const para of paragraphs.slice(-3)) {
|
|
631
|
+
if (this.containsSignals(para.toLowerCase(), ctaSignals)) {
|
|
632
|
+
callToAction = this.extractFirstSentence(para);
|
|
294
633
|
break;
|
|
295
634
|
}
|
|
296
635
|
}
|
|
297
|
-
return {
|
|
636
|
+
return { callToAction };
|
|
298
637
|
}
|
|
299
638
|
/**
|
|
300
|
-
* Extract key messages (max 3 - Rule of Three)
|
|
639
|
+
* Extract key messages (max 3 - Rule of Three)
|
|
301
640
|
*/
|
|
302
|
-
extractKeyMessages(text
|
|
641
|
+
extractKeyMessages(text) {
|
|
303
642
|
const messages = [];
|
|
304
|
-
const
|
|
305
|
-
if (
|
|
306
|
-
for (const match of
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
309
|
-
messages.push(
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
const headerMatches = text.match(/\[HEADER\](.+)/g);
|
|
314
|
-
if (headerMatches && messages.length < 3) {
|
|
315
|
-
for (const match of headerMatches.slice(0, 3 - messages.length)) {
|
|
316
|
-
const content = match.replace("[HEADER]", "").trim();
|
|
317
|
-
if (content.length > 5 && content.length < 80) {
|
|
318
|
-
messages.push(content);
|
|
643
|
+
const h2Matches = text.match(/^##\s+(.+)$/gm);
|
|
644
|
+
if (h2Matches) {
|
|
645
|
+
for (const match of h2Matches.slice(0, 3)) {
|
|
646
|
+
const msg = match.replace(/^##\s+/, "").replace(/\*\*/g, "").trim();
|
|
647
|
+
if (msg.length > 5 && msg.length < 80) {
|
|
648
|
+
messages.push(msg);
|
|
319
649
|
}
|
|
320
650
|
}
|
|
321
651
|
}
|
|
322
652
|
if (messages.length < 3) {
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
messages.push(sentence);
|
|
332
|
-
if (messages.length >= 3) break;
|
|
653
|
+
const boldMatches = text.match(/\*\*(.+?)\*\*/g);
|
|
654
|
+
if (boldMatches) {
|
|
655
|
+
for (const match of boldMatches) {
|
|
656
|
+
const msg = match.replace(/\*\*/g, "").trim();
|
|
657
|
+
if (msg.length > 10 && msg.length < 80 && !messages.includes(msg)) {
|
|
658
|
+
messages.push(msg);
|
|
659
|
+
if (messages.length >= 3) break;
|
|
660
|
+
}
|
|
333
661
|
}
|
|
334
662
|
}
|
|
335
663
|
}
|
|
336
664
|
return messages.slice(0, 3);
|
|
337
665
|
}
|
|
338
666
|
/**
|
|
339
|
-
*
|
|
667
|
+
* Extract data points (metrics with values)
|
|
340
668
|
*/
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
669
|
+
extractDataPoints(text) {
|
|
670
|
+
const dataPoints = [];
|
|
671
|
+
const dollarMatches = text.match(/\$[\d,]+(?:\.\d+)?[MBK]?(?:\s*(?:million|billion|thousand))?/gi);
|
|
672
|
+
if (dollarMatches) {
|
|
673
|
+
for (const match of dollarMatches.slice(0, 4)) {
|
|
674
|
+
const context = this.getContextAroundMatch(text, match);
|
|
675
|
+
dataPoints.push({
|
|
676
|
+
value: match,
|
|
677
|
+
label: this.extractLabelFromContext(context)
|
|
678
|
+
});
|
|
347
679
|
}
|
|
348
680
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
681
|
+
const percentMatches = text.match(/\d+(?:\.\d+)?%/g);
|
|
682
|
+
if (percentMatches) {
|
|
683
|
+
for (const match of percentMatches.slice(0, 4)) {
|
|
684
|
+
if (!dataPoints.some((d) => d.value === match)) {
|
|
685
|
+
const context = this.getContextAroundMatch(text, match);
|
|
686
|
+
dataPoints.push({
|
|
687
|
+
value: match,
|
|
688
|
+
label: this.extractLabelFromContext(context)
|
|
689
|
+
});
|
|
356
690
|
}
|
|
357
691
|
}
|
|
358
692
|
}
|
|
359
|
-
return
|
|
693
|
+
return dataPoints.slice(0, 6);
|
|
360
694
|
}
|
|
361
695
|
/**
|
|
362
|
-
*
|
|
696
|
+
* Get context around a match
|
|
363
697
|
*/
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
if (title.toLowerCase().includes("should")) {
|
|
372
|
-
title = title.replace(/we should|you should|should/gi, "").trim();
|
|
373
|
-
return this.capitalizeFirst(title);
|
|
374
|
-
}
|
|
375
|
-
if (title.toLowerCase().includes("need to")) {
|
|
376
|
-
title = title.replace(/we need to|you need to|need to/gi, "").trim();
|
|
377
|
-
return this.capitalizeFirst(title);
|
|
378
|
-
}
|
|
379
|
-
if (title.length < 50) {
|
|
380
|
-
return title;
|
|
381
|
-
}
|
|
382
|
-
return this.truncateToWords(title, 8);
|
|
698
|
+
getContextAroundMatch(text, match) {
|
|
699
|
+
const index = text.indexOf(match);
|
|
700
|
+
if (index === -1) return "";
|
|
701
|
+
const start = Math.max(0, index - 50);
|
|
702
|
+
const end = Math.min(text.length, index + match.length + 50);
|
|
703
|
+
return text.slice(start, end);
|
|
383
704
|
}
|
|
384
705
|
/**
|
|
385
|
-
*
|
|
706
|
+
* Extract a label from surrounding context
|
|
707
|
+
* Fixes the "Century Interactive |" garbage issue by stripping table syntax
|
|
386
708
|
*/
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
"amazing",
|
|
392
|
-
"incredible",
|
|
393
|
-
"remarkable",
|
|
394
|
-
"stunning",
|
|
395
|
-
"imagine",
|
|
396
|
-
"what if",
|
|
397
|
-
"breakthrough",
|
|
398
|
-
"revolutionary",
|
|
399
|
-
"never before",
|
|
400
|
-
"first time",
|
|
401
|
-
"unprecedented",
|
|
402
|
-
"game-changing",
|
|
403
|
-
"dramatic"
|
|
404
|
-
];
|
|
405
|
-
for (const para of paragraphs) {
|
|
406
|
-
if (this.containsSignals(para.toLowerCase(), starSignals)) {
|
|
407
|
-
const moment = this.truncateToSentence(para, 120);
|
|
408
|
-
if (moment.length > 20) {
|
|
409
|
-
starMoments.push(moment);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
const statPattern = /\d+[%xX]|\$[\d,]+(?:\s*(?:million|billion|trillion))?|\d+(?:\s*(?:million|billion|trillion))/g;
|
|
414
|
-
for (const para of paragraphs) {
|
|
415
|
-
if (statPattern.test(para) && starMoments.length < 5) {
|
|
416
|
-
const moment = this.truncateToSentence(para, 100);
|
|
417
|
-
if (!starMoments.includes(moment)) {
|
|
418
|
-
starMoments.push(moment);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return starMoments.slice(0, 5);
|
|
709
|
+
extractLabelFromContext(context) {
|
|
710
|
+
let cleaned = context.replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\s{2,}/g, " ").replace(/\*\*/g, "").replace(/#+\s*/g, "").trim();
|
|
711
|
+
const words = cleaned.split(/\s+/).filter((w) => w.length > 2 && !w.match(/^\d/));
|
|
712
|
+
return words.slice(0, 4).join(" ") || "Value";
|
|
423
713
|
}
|
|
424
714
|
/**
|
|
425
|
-
*
|
|
715
|
+
* Check if text contains any of the signals
|
|
426
716
|
*/
|
|
427
|
-
estimateSlideCount(text, paragraphs) {
|
|
428
|
-
const wordCount = text.split(/\s+/).length;
|
|
429
|
-
const headerCount = (text.match(/\[HEADER\]/g) ?? []).length;
|
|
430
|
-
const bulletGroups = (text.match(/\[BULLET\]/g) ?? []).length / 4;
|
|
431
|
-
const wordBasedEstimate = Math.ceil(wordCount / 35);
|
|
432
|
-
const estimate = Math.max(
|
|
433
|
-
5,
|
|
434
|
-
// Minimum 5 slides
|
|
435
|
-
Math.ceil((wordBasedEstimate + headerCount + bulletGroups) / 2)
|
|
436
|
-
);
|
|
437
|
-
return Math.min(estimate, 30);
|
|
438
|
-
}
|
|
439
|
-
// === Helper Methods ===
|
|
440
717
|
containsSignals(text, signals) {
|
|
441
|
-
|
|
442
|
-
return signals.some((signal) => lowerText.includes(signal));
|
|
443
|
-
}
|
|
444
|
-
extractRelevantSentence(paragraph, signals) {
|
|
445
|
-
const sentences = paragraph.split(/[.!?]+/);
|
|
446
|
-
for (const sentence of sentences) {
|
|
447
|
-
if (this.containsSignals(sentence.toLowerCase(), signals)) {
|
|
448
|
-
return sentence.trim();
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
return this.truncateToSentence(paragraph, 150);
|
|
718
|
+
return signals.some((signal) => text.includes(signal));
|
|
452
719
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (lastBoundary > maxLength * 0.5) {
|
|
463
|
-
return text.slice(0, lastBoundary + 1).trim();
|
|
464
|
-
}
|
|
465
|
-
return truncated.trim() + "...";
|
|
466
|
-
}
|
|
467
|
-
truncateToWords(text, maxWords) {
|
|
468
|
-
const words = text.split(/\s+/);
|
|
469
|
-
if (words.length <= maxWords) {
|
|
470
|
-
return text;
|
|
720
|
+
/**
|
|
721
|
+
* Extract first meaningful sentence from text
|
|
722
|
+
*/
|
|
723
|
+
extractFirstSentence(text) {
|
|
724
|
+
const cleaned = text.replace(/^#+\s*/gm, "").replace(/\*\*/g, "").replace(/\|/g, " ").trim();
|
|
725
|
+
const sentences = cleaned.split(/[.!?]+/);
|
|
726
|
+
const first = sentences[0]?.trim();
|
|
727
|
+
if (first && first.length > 10) {
|
|
728
|
+
return first.slice(0, 150);
|
|
471
729
|
}
|
|
472
|
-
return
|
|
473
|
-
}
|
|
474
|
-
capitalizeFirst(text) {
|
|
475
|
-
if (!text) return "";
|
|
476
|
-
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
730
|
+
return cleaned.slice(0, 150);
|
|
477
731
|
}
|
|
478
732
|
};
|
|
479
733
|
|
|
480
734
|
// src/core/SlideFactory.ts
|
|
481
735
|
var SlideFactory = class {
|
|
482
736
|
templates;
|
|
737
|
+
usedContent = /* @__PURE__ */ new Set();
|
|
483
738
|
constructor() {
|
|
484
739
|
this.templates = this.initializeTemplates();
|
|
485
740
|
}
|
|
741
|
+
/**
|
|
742
|
+
* Check if content has already been used (deduplication)
|
|
743
|
+
*/
|
|
744
|
+
isContentUsed(content) {
|
|
745
|
+
if (!content) return true;
|
|
746
|
+
const normalized = content.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 50);
|
|
747
|
+
if (this.usedContent.has(normalized)) {
|
|
748
|
+
return true;
|
|
749
|
+
}
|
|
750
|
+
this.usedContent.add(normalized);
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
486
753
|
/**
|
|
487
754
|
* Create slides from analyzed content.
|
|
488
755
|
*/
|
|
489
756
|
async createSlides(analysis, mode) {
|
|
490
757
|
const slides = [];
|
|
491
758
|
let slideIndex = 0;
|
|
759
|
+
this.usedContent.clear();
|
|
492
760
|
slides.push(this.createTitleSlide(slideIndex++, analysis));
|
|
761
|
+
this.isContentUsed(analysis.titles[0] ?? "");
|
|
493
762
|
if (mode === "business" && analysis.keyMessages.length >= 2) {
|
|
494
763
|
slides.push(this.createAgendaSlide(slideIndex++, analysis));
|
|
495
764
|
}
|
|
496
|
-
if (analysis.scqa.situation) {
|
|
765
|
+
if (analysis.scqa.situation && !this.isContentUsed(analysis.scqa.situation)) {
|
|
497
766
|
slides.push(this.createContextSlide(slideIndex++, analysis, mode));
|
|
498
767
|
}
|
|
499
|
-
if (analysis.scqa.complication) {
|
|
768
|
+
if (analysis.scqa.complication && !this.isContentUsed(analysis.scqa.complication)) {
|
|
500
769
|
slides.push(this.createProblemSlide(slideIndex++, analysis, mode));
|
|
501
770
|
}
|
|
502
771
|
for (const message of analysis.keyMessages) {
|
|
503
|
-
|
|
772
|
+
if (!this.isContentUsed(message)) {
|
|
773
|
+
slides.push(this.createMessageSlide(slideIndex++, message, mode));
|
|
774
|
+
}
|
|
504
775
|
}
|
|
505
776
|
for (const starMoment of analysis.starMoments.slice(0, 2)) {
|
|
506
|
-
|
|
777
|
+
if (!this.isContentUsed(starMoment)) {
|
|
778
|
+
slides.push(this.createStarMomentSlide(slideIndex++, starMoment, mode));
|
|
779
|
+
}
|
|
507
780
|
}
|
|
508
|
-
if (analysis.scqa.answer) {
|
|
781
|
+
if (analysis.scqa.answer && !this.isContentUsed(analysis.scqa.answer)) {
|
|
509
782
|
slides.push(this.createSolutionSlide(slideIndex++, analysis, mode));
|
|
510
783
|
}
|
|
511
|
-
if (analysis.sparkline.callToAdventure) {
|
|
784
|
+
if (analysis.sparkline.callToAdventure && !this.isContentUsed(analysis.sparkline.callToAdventure)) {
|
|
512
785
|
slides.push(this.createCTASlide(slideIndex++, analysis, mode));
|
|
513
786
|
}
|
|
514
787
|
slides.push(this.createThankYouSlide(slideIndex++));
|
|
@@ -518,13 +791,18 @@ var SlideFactory = class {
|
|
|
518
791
|
* Create a title slide.
|
|
519
792
|
*/
|
|
520
793
|
createTitleSlide(index, analysis) {
|
|
521
|
-
|
|
794
|
+
let subtitle = "";
|
|
795
|
+
if (analysis.scqa.answer && analysis.scqa.answer.length > 10) {
|
|
796
|
+
subtitle = analysis.scqa.answer;
|
|
797
|
+
} else if (analysis.starMoments.length > 0 && analysis.starMoments[0]) {
|
|
798
|
+
subtitle = analysis.starMoments[0];
|
|
799
|
+
}
|
|
522
800
|
return {
|
|
523
801
|
index,
|
|
524
802
|
type: "title",
|
|
525
803
|
data: {
|
|
526
804
|
title: analysis.titles[0] ?? "Presentation",
|
|
527
|
-
subtitle: this.truncate(subtitle,
|
|
805
|
+
subtitle: this.truncate(subtitle, 80),
|
|
528
806
|
keyMessage: analysis.scqa.answer
|
|
529
807
|
},
|
|
530
808
|
classes: ["slide-title"]
|
|
@@ -804,14 +1082,22 @@ var SlideFactory = class {
|
|
|
804
1082
|
return templates;
|
|
805
1083
|
}
|
|
806
1084
|
// === Helper Methods ===
|
|
1085
|
+
/**
|
|
1086
|
+
* Clean text by removing all content markers.
|
|
1087
|
+
*/
|
|
1088
|
+
cleanText(text) {
|
|
1089
|
+
if (!text) return "";
|
|
1090
|
+
return text.replace(/\[HEADER\]\s*/g, "").replace(/\[BULLET\]\s*/g, "").replace(/\[NUMBERED\]\s*/g, "").replace(/\[EMPHASIS\]/g, "").replace(/\[\/EMPHASIS\]/g, "").replace(/\[CODE BLOCK\]/g, "").replace(/\[IMAGE\]/g, "").replace(/\s+/g, " ").trim();
|
|
1091
|
+
}
|
|
807
1092
|
/**
|
|
808
1093
|
* Truncate text to max length at word boundary.
|
|
809
1094
|
*/
|
|
810
1095
|
truncate(text, maxLength) {
|
|
811
|
-
|
|
812
|
-
|
|
1096
|
+
const cleanedText = this.cleanText(text);
|
|
1097
|
+
if (!cleanedText || cleanedText.length <= maxLength) {
|
|
1098
|
+
return cleanedText;
|
|
813
1099
|
}
|
|
814
|
-
const truncated =
|
|
1100
|
+
const truncated = cleanedText.slice(0, maxLength);
|
|
815
1101
|
const lastSpace = truncated.lastIndexOf(" ");
|
|
816
1102
|
if (lastSpace > maxLength * 0.7) {
|
|
817
1103
|
return truncated.slice(0, lastSpace) + "...";
|
|
@@ -822,11 +1108,12 @@ var SlideFactory = class {
|
|
|
822
1108
|
* Extract an action title from a message.
|
|
823
1109
|
*/
|
|
824
1110
|
extractActionTitle(message) {
|
|
825
|
-
const
|
|
1111
|
+
const cleanedMessage = this.cleanText(message);
|
|
1112
|
+
const firstSentence = cleanedMessage.split(/[.!?]/)[0];
|
|
826
1113
|
if (firstSentence && firstSentence.length <= 50) {
|
|
827
1114
|
return firstSentence;
|
|
828
1115
|
}
|
|
829
|
-
const words =
|
|
1116
|
+
const words = cleanedMessage.split(/\s+/).slice(0, 6);
|
|
830
1117
|
return words.join(" ");
|
|
831
1118
|
}
|
|
832
1119
|
/**
|
|
@@ -836,9 +1123,10 @@ var SlideFactory = class {
|
|
|
836
1123
|
if (!text) return [];
|
|
837
1124
|
const bulletMatches = text.match(/\[BULLET\]\s*(.+)/g);
|
|
838
1125
|
if (bulletMatches && bulletMatches.length > 0) {
|
|
839
|
-
return bulletMatches.map((b) =>
|
|
1126
|
+
return bulletMatches.map((b) => this.cleanText(b)).slice(0, 5);
|
|
840
1127
|
}
|
|
841
|
-
const
|
|
1128
|
+
const cleanedText = this.cleanText(text);
|
|
1129
|
+
const sentences = cleanedText.split(/[.!?]+/).filter((s) => s.trim().length > 10);
|
|
842
1130
|
return sentences.slice(0, 5).map((s) => s.trim());
|
|
843
1131
|
}
|
|
844
1132
|
/**
|
|
@@ -1745,19 +2033,28 @@ var ScoreCalculator = class {
|
|
|
1745
2033
|
import { chromium } from "playwright";
|
|
1746
2034
|
var QAEngine = class {
|
|
1747
2035
|
browser = null;
|
|
2036
|
+
kb;
|
|
1748
2037
|
/**
|
|
1749
|
-
*
|
|
2038
|
+
* Initialize KnowledgeGateway
|
|
2039
|
+
*/
|
|
2040
|
+
async initialize() {
|
|
2041
|
+
this.kb = await getKnowledgeGateway();
|
|
2042
|
+
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Validate a presentation using KB-driven quality metrics
|
|
1750
2045
|
*/
|
|
1751
2046
|
async validate(presentation, options) {
|
|
2047
|
+
await this.initialize();
|
|
1752
2048
|
const html = typeof presentation === "string" ? presentation : presentation.toString("utf-8");
|
|
1753
2049
|
const mode = options?.mode ?? "keynote";
|
|
2050
|
+
const qualityMetrics = this.kb.getQualityMetrics();
|
|
1754
2051
|
await this.initBrowser();
|
|
1755
2052
|
try {
|
|
1756
2053
|
const [visualResults, contentResults, expertResults, accessibilityResults] = await Promise.all([
|
|
1757
|
-
this.runVisualTests(html, mode),
|
|
1758
|
-
this.runContentTests(html, mode),
|
|
2054
|
+
this.runVisualTests(html, mode, qualityMetrics),
|
|
2055
|
+
this.runContentTests(html, mode, qualityMetrics),
|
|
1759
2056
|
this.runExpertTests(html, mode),
|
|
1760
|
-
this.runAccessibilityTests(html)
|
|
2057
|
+
this.runAccessibilityTests(html, qualityMetrics)
|
|
1761
2058
|
]);
|
|
1762
2059
|
const issues = this.collectIssues(visualResults, contentResults, expertResults, accessibilityResults);
|
|
1763
2060
|
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
@@ -1775,18 +2072,18 @@ var QAEngine = class {
|
|
|
1775
2072
|
}
|
|
1776
2073
|
}
|
|
1777
2074
|
/**
|
|
1778
|
-
* Calculate overall QA score
|
|
2075
|
+
* Calculate overall QA score using KB-driven weights
|
|
1779
2076
|
*/
|
|
1780
2077
|
calculateScore(results) {
|
|
1781
2078
|
const weights = {
|
|
1782
2079
|
visual: 0.35,
|
|
1783
|
-
// 35%
|
|
2080
|
+
// 35% - Design quality
|
|
1784
2081
|
content: 0.3,
|
|
1785
|
-
// 30%
|
|
2082
|
+
// 30% - Content quality
|
|
1786
2083
|
expert: 0.25,
|
|
1787
|
-
// 25%
|
|
2084
|
+
// 25% - Expert methodology
|
|
1788
2085
|
accessibility: 0.1
|
|
1789
|
-
// 10%
|
|
2086
|
+
// 10% - Accessibility
|
|
1790
2087
|
};
|
|
1791
2088
|
const visualScore = this.calculateVisualScore(results.visual);
|
|
1792
2089
|
const contentScore = this.calculateContentScore(results.content);
|
|
@@ -1833,13 +2130,15 @@ var QAEngine = class {
|
|
|
1833
2130
|
};
|
|
1834
2131
|
}
|
|
1835
2132
|
// ===========================================================================
|
|
1836
|
-
// VISUAL TESTS
|
|
2133
|
+
// VISUAL TESTS - KB-DRIVEN
|
|
1837
2134
|
// ===========================================================================
|
|
1838
|
-
async runVisualTests(html, mode) {
|
|
2135
|
+
async runVisualTests(html, mode, qualityMetrics) {
|
|
1839
2136
|
const page = await this.browser.newPage();
|
|
1840
2137
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
1841
2138
|
await page.setContent(html);
|
|
1842
2139
|
await page.waitForTimeout(1e3);
|
|
2140
|
+
const modeMetrics = mode === "keynote" ? qualityMetrics.keynote_mode : qualityMetrics.business_mode;
|
|
2141
|
+
const minWhitespace = this.kb.getWhitespaceRequirement(mode) * 100;
|
|
1843
2142
|
const slideCount = await page.evaluate(() => {
|
|
1844
2143
|
return window.Reveal?.getTotalSlides?.() ?? document.querySelectorAll(".slides > section").length;
|
|
1845
2144
|
});
|
|
@@ -1905,9 +2204,8 @@ var QAEngine = class {
|
|
|
1905
2204
|
}, { slideIndex: i });
|
|
1906
2205
|
if (slideAnalysis) {
|
|
1907
2206
|
const issues = [];
|
|
1908
|
-
const minWhitespace = mode === "keynote" ? 40 : 25;
|
|
1909
2207
|
if (slideAnalysis.whitespace < minWhitespace) {
|
|
1910
|
-
issues.push(`Whitespace ${slideAnalysis.whitespace}% below ${minWhitespace}% minimum`);
|
|
2208
|
+
issues.push(`Whitespace ${slideAnalysis.whitespace}% below ${minWhitespace}% minimum (KB-defined)`);
|
|
1911
2209
|
}
|
|
1912
2210
|
if (slideAnalysis.whitespace > 80) {
|
|
1913
2211
|
issues.push(`Whitespace ${slideAnalysis.whitespace}% - slide appears sparse`);
|
|
@@ -1962,34 +2260,35 @@ var QAEngine = class {
|
|
|
1962
2260
|
};
|
|
1963
2261
|
}
|
|
1964
2262
|
// ===========================================================================
|
|
1965
|
-
// CONTENT TESTS
|
|
2263
|
+
// CONTENT TESTS - KB-DRIVEN
|
|
1966
2264
|
// ===========================================================================
|
|
1967
|
-
async runContentTests(html, mode) {
|
|
2265
|
+
async runContentTests(html, mode, qualityMetrics) {
|
|
1968
2266
|
const page = await this.browser.newPage();
|
|
1969
2267
|
await page.setContent(html);
|
|
1970
2268
|
await page.waitForTimeout(500);
|
|
1971
|
-
const
|
|
2269
|
+
const wordLimits = this.kb.getWordLimits(mode);
|
|
2270
|
+
const glanceTest = this.kb.getDuarteGlanceTest();
|
|
2271
|
+
const results = await page.evaluate((params) => {
|
|
2272
|
+
const { targetMode, maxWords, minWords, glanceWordLimit } = params;
|
|
1972
2273
|
const slides = document.querySelectorAll(".slides > section");
|
|
1973
2274
|
const perSlide = [];
|
|
1974
|
-
const
|
|
2275
|
+
const glanceTestResults = [];
|
|
1975
2276
|
const signalNoise = [];
|
|
1976
2277
|
const oneIdea = [];
|
|
1977
2278
|
slides.forEach((slide, index) => {
|
|
1978
2279
|
const text = slide.innerText || "";
|
|
1979
2280
|
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
1980
2281
|
const wordCount = words.length;
|
|
1981
|
-
const maxWords = targetMode === "keynote" ? 25 : 80;
|
|
1982
|
-
const minWords = targetMode === "business" ? 20 : 0;
|
|
1983
2282
|
const withinLimit = wordCount <= maxWords && wordCount >= minWords;
|
|
1984
2283
|
const title = slide.querySelector("h2")?.textContent || "";
|
|
1985
2284
|
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);
|
|
1986
2285
|
const hasInsight = title.length > 30 && hasVerb;
|
|
1987
2286
|
const issues = [];
|
|
1988
2287
|
if (!withinLimit) {
|
|
1989
|
-
issues.push(`Word count ${wordCount} outside ${minWords}-${maxWords} range`);
|
|
2288
|
+
issues.push(`Word count ${wordCount} outside ${minWords}-${maxWords} range (KB-defined)`);
|
|
1990
2289
|
}
|
|
1991
2290
|
if (targetMode === "business" && !hasInsight && index > 0) {
|
|
1992
|
-
issues.push("Title is not action-oriented (missing insight)");
|
|
2291
|
+
issues.push("Title is not action-oriented (missing insight per Minto)");
|
|
1993
2292
|
}
|
|
1994
2293
|
perSlide.push({
|
|
1995
2294
|
slideIndex: index,
|
|
@@ -2002,12 +2301,12 @@ var QAEngine = class {
|
|
|
2002
2301
|
const keyMessage = prominentElement?.textContent?.trim() || "";
|
|
2003
2302
|
const keyWordCount = keyMessage.split(/\s+/).filter((w) => w.length > 0).length;
|
|
2004
2303
|
const readingTime = keyWordCount / 4.2;
|
|
2005
|
-
|
|
2304
|
+
glanceTestResults.push({
|
|
2006
2305
|
slideIndex: index,
|
|
2007
2306
|
keyMessage,
|
|
2008
2307
|
wordCount: keyWordCount,
|
|
2009
2308
|
readingTime: Math.round(readingTime * 10) / 10,
|
|
2010
|
-
passed: readingTime <= 3 && keyWordCount <=
|
|
2309
|
+
passed: readingTime <= 3 && keyWordCount <= glanceWordLimit,
|
|
2011
2310
|
recommendation: readingTime > 3 ? `Shorten to ${Math.floor(3 * 4.2)} words or less` : void 0
|
|
2012
2311
|
});
|
|
2013
2312
|
const elements = slide.querySelectorAll("h1, h2, h3, p, li, img");
|
|
@@ -2034,35 +2333,143 @@ var QAEngine = class {
|
|
|
2034
2333
|
conflictingIdeas: ideaCount > 2 ? ["Multiple competing ideas detected"] : void 0
|
|
2035
2334
|
});
|
|
2036
2335
|
});
|
|
2037
|
-
return { perSlide, glanceTest, signalNoise, oneIdea };
|
|
2038
|
-
},
|
|
2336
|
+
return { perSlide, glanceTest: glanceTestResults, signalNoise, oneIdea };
|
|
2337
|
+
}, {
|
|
2338
|
+
targetMode: mode,
|
|
2339
|
+
maxWords: wordLimits.max,
|
|
2340
|
+
minWords: mode === "business" ? 20 : 0,
|
|
2341
|
+
glanceWordLimit: glanceTest.wordLimit
|
|
2342
|
+
});
|
|
2039
2343
|
await page.close();
|
|
2040
2344
|
return results;
|
|
2041
2345
|
}
|
|
2042
2346
|
// ===========================================================================
|
|
2043
|
-
// EXPERT TESTS
|
|
2347
|
+
// EXPERT TESTS - KB-DRIVEN
|
|
2044
2348
|
// ===========================================================================
|
|
2045
2349
|
async runExpertTests(html, mode) {
|
|
2350
|
+
const duartePrinciples = this.kb.getExpertPrinciples("nancy_duarte");
|
|
2351
|
+
const reynoldsPrinciples = this.kb.getExpertPrinciples("garr_reynolds");
|
|
2352
|
+
const galloPrinciples = this.kb.getExpertPrinciples("carmine_gallo");
|
|
2353
|
+
const andersonPrinciples = this.kb.getExpertPrinciples("chris_anderson");
|
|
2354
|
+
return {
|
|
2355
|
+
duarte: this.validateDuartePrinciples(html, duartePrinciples),
|
|
2356
|
+
reynolds: this.validateReynoldsPrinciples(html, reynoldsPrinciples),
|
|
2357
|
+
gallo: this.validateGalloPrinciples(html, galloPrinciples),
|
|
2358
|
+
anderson: this.validateAndersonPrinciples(html, andersonPrinciples)
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
validateDuartePrinciples(html, principles) {
|
|
2362
|
+
const violations = [];
|
|
2363
|
+
let score = 100;
|
|
2364
|
+
const slideMatches = html.match(/<section[^>]*>[\s\S]*?<\/section>/gi) || [];
|
|
2365
|
+
slideMatches.forEach((slide, index) => {
|
|
2366
|
+
const h1Match = slide.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i);
|
|
2367
|
+
const h2Match = slide.match(/<h2[^>]*>([\s\S]*?)<\/h2>/i);
|
|
2368
|
+
const keyMessage = (h1Match?.[1] || h2Match?.[1] || "").replace(/<[^>]+>/g, "").trim();
|
|
2369
|
+
const wordCount = keyMessage.split(/\s+/).filter((w) => w.length > 0).length;
|
|
2370
|
+
if (wordCount > 25) {
|
|
2371
|
+
violations.push(`Slide ${index + 1}: Key message exceeds 25 words (Duarte Glance Test)`);
|
|
2372
|
+
score -= 5;
|
|
2373
|
+
}
|
|
2374
|
+
});
|
|
2375
|
+
const hasStarMoment = html.includes("callout") || html.includes("highlight") || html.includes("big-idea");
|
|
2376
|
+
if (!hasStarMoment && slideMatches.length > 5) {
|
|
2377
|
+
violations.push("Missing STAR moment (Something They'll Always Remember)");
|
|
2378
|
+
score -= 10;
|
|
2379
|
+
}
|
|
2046
2380
|
return {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2381
|
+
expertName: "Nancy Duarte",
|
|
2382
|
+
principlesChecked: ["Glance Test", "STAR Moment", "Sparkline Structure"],
|
|
2383
|
+
passed: score >= 80,
|
|
2384
|
+
score: Math.max(0, score),
|
|
2385
|
+
violations
|
|
2051
2386
|
};
|
|
2052
2387
|
}
|
|
2053
|
-
|
|
2388
|
+
validateReynoldsPrinciples(html, principles) {
|
|
2389
|
+
const violations = [];
|
|
2390
|
+
let score = 100;
|
|
2391
|
+
const decorativeElements = (html.match(/class="[^"]*decorative[^"]*"/gi) || []).length;
|
|
2392
|
+
const randomImages = (html.match(/picsum\.photos/gi) || []).length;
|
|
2393
|
+
if (randomImages > 0) {
|
|
2394
|
+
violations.push(`Found ${randomImages} random stock images (Reynolds: only use purposeful images)`);
|
|
2395
|
+
score -= randomImages * 10;
|
|
2396
|
+
}
|
|
2397
|
+
if (decorativeElements > 3) {
|
|
2398
|
+
violations.push(`Too many decorative elements (${decorativeElements}) - reduces signal-to-noise ratio`);
|
|
2399
|
+
score -= 10;
|
|
2400
|
+
}
|
|
2401
|
+
const columnLayouts = (html.match(/class="[^"]*columns[^"]*"/gi) || []).length;
|
|
2402
|
+
const slideCount = (html.match(/<section/gi) || []).length;
|
|
2403
|
+
if (columnLayouts > slideCount * 0.5) {
|
|
2404
|
+
violations.push("Overuse of column layouts - simplify per Reynolds");
|
|
2405
|
+
score -= 10;
|
|
2406
|
+
}
|
|
2054
2407
|
return {
|
|
2055
|
-
expertName:
|
|
2056
|
-
principlesChecked:
|
|
2408
|
+
expertName: "Garr Reynolds",
|
|
2409
|
+
principlesChecked: ["Signal-to-Noise", "Simplicity", "Picture Superiority"],
|
|
2057
2410
|
passed: score >= 80,
|
|
2058
|
-
score,
|
|
2059
|
-
violations
|
|
2411
|
+
score: Math.max(0, score),
|
|
2412
|
+
violations
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
validateGalloPrinciples(html, principles) {
|
|
2416
|
+
const violations = [];
|
|
2417
|
+
let score = 100;
|
|
2418
|
+
const bulletLists = html.match(/<ul[^>]*>[\s\S]*?<\/ul>/gi) || [];
|
|
2419
|
+
bulletLists.forEach((list, index) => {
|
|
2420
|
+
const bullets = (list.match(/<li/gi) || []).length;
|
|
2421
|
+
if (bullets > 5) {
|
|
2422
|
+
violations.push(`Bullet list ${index + 1} has ${bullets} items (Gallo: max 3-5)`);
|
|
2423
|
+
score -= 5;
|
|
2424
|
+
}
|
|
2425
|
+
});
|
|
2426
|
+
const hasQuote = html.includes("blockquote") || html.includes("quote");
|
|
2427
|
+
const hasStory = html.toLowerCase().includes("story") || html.toLowerCase().includes("journey");
|
|
2428
|
+
if (!hasQuote && !hasStory) {
|
|
2429
|
+
violations.push("Missing emotional connection elements (quotes, stories)");
|
|
2430
|
+
score -= 10;
|
|
2431
|
+
}
|
|
2432
|
+
return {
|
|
2433
|
+
expertName: "Carmine Gallo",
|
|
2434
|
+
principlesChecked: ["Rule of Three", "Emotional Connection", "Headline Power"],
|
|
2435
|
+
passed: score >= 80,
|
|
2436
|
+
score: Math.max(0, score),
|
|
2437
|
+
violations
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
validateAndersonPrinciples(html, principles) {
|
|
2441
|
+
const violations = [];
|
|
2442
|
+
let score = 100;
|
|
2443
|
+
const slideMatches = html.match(/<section[^>]*>[\s\S]*?<\/section>/gi) || [];
|
|
2444
|
+
slideMatches.forEach((slide, index) => {
|
|
2445
|
+
const headings = (slide.match(/<h[12][^>]*>/gi) || []).length;
|
|
2446
|
+
if (headings > 2) {
|
|
2447
|
+
violations.push(`Slide ${index + 1}: Multiple ideas detected (Anderson: one idea per slide)`);
|
|
2448
|
+
score -= 5;
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
const jargonPatterns = ["synergy", "leverage", "paradigm", "holistic", "streamline"];
|
|
2452
|
+
let jargonCount = 0;
|
|
2453
|
+
jargonPatterns.forEach((pattern) => {
|
|
2454
|
+
const matches = html.toLowerCase().match(new RegExp(pattern, "gi")) || [];
|
|
2455
|
+
jargonCount += matches.length;
|
|
2456
|
+
});
|
|
2457
|
+
if (jargonCount > 5) {
|
|
2458
|
+
violations.push(`Excessive jargon detected (${jargonCount} instances) - simplify language`);
|
|
2459
|
+
score -= 10;
|
|
2460
|
+
}
|
|
2461
|
+
return {
|
|
2462
|
+
expertName: "Chris Anderson",
|
|
2463
|
+
principlesChecked: ["One Idea", "Clarity", "Throughline"],
|
|
2464
|
+
passed: score >= 80,
|
|
2465
|
+
score: Math.max(0, score),
|
|
2466
|
+
violations
|
|
2060
2467
|
};
|
|
2061
2468
|
}
|
|
2062
2469
|
// ===========================================================================
|
|
2063
|
-
// ACCESSIBILITY TESTS
|
|
2470
|
+
// ACCESSIBILITY TESTS - KB-DRIVEN
|
|
2064
2471
|
// ===========================================================================
|
|
2065
|
-
async runAccessibilityTests(html) {
|
|
2472
|
+
async runAccessibilityTests(html, qualityMetrics) {
|
|
2066
2473
|
const page = await this.browser.newPage();
|
|
2067
2474
|
await page.setContent(html);
|
|
2068
2475
|
await page.waitForTimeout(500);
|
|
@@ -2112,7 +2519,7 @@ var QAEngine = class {
|
|
|
2112
2519
|
};
|
|
2113
2520
|
}
|
|
2114
2521
|
// ===========================================================================
|
|
2115
|
-
// SCORING
|
|
2522
|
+
// SCORING - KB-DRIVEN
|
|
2116
2523
|
// ===========================================================================
|
|
2117
2524
|
calculateVisualScore(results) {
|
|
2118
2525
|
let score = 100;
|
|
@@ -2236,6 +2643,7 @@ var QAEngine = class {
|
|
|
2236
2643
|
// src/generators/html/RevealJsGenerator.ts
|
|
2237
2644
|
var RevealJsGenerator = class {
|
|
2238
2645
|
templateEngine;
|
|
2646
|
+
kb;
|
|
2239
2647
|
defaultRevealConfig = {
|
|
2240
2648
|
revealVersion: "5.0.4",
|
|
2241
2649
|
hash: true,
|
|
@@ -2254,20 +2662,32 @@ var RevealJsGenerator = class {
|
|
|
2254
2662
|
constructor() {
|
|
2255
2663
|
this.templateEngine = new TemplateEngine();
|
|
2256
2664
|
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Initialize KnowledgeGateway
|
|
2667
|
+
*/
|
|
2668
|
+
async initialize() {
|
|
2669
|
+
this.kb = await getKnowledgeGateway();
|
|
2670
|
+
}
|
|
2257
2671
|
/**
|
|
2258
2672
|
* Generate complete Reveal.js HTML presentation.
|
|
2259
2673
|
*/
|
|
2260
2674
|
async generate(slides, config) {
|
|
2675
|
+
await this.initialize();
|
|
2261
2676
|
const templateConfig = {};
|
|
2262
2677
|
if (config.theme) templateConfig.theme = config.theme;
|
|
2263
2678
|
if (config.customTemplates) templateConfig.customTemplates = config.customTemplates;
|
|
2264
2679
|
const slideHtml = this.templateEngine.renderAll(slides, templateConfig);
|
|
2680
|
+
const presentationType = this.detectPresentationType(config);
|
|
2681
|
+
const palette = this.kb.getRecommendedPalette(presentationType);
|
|
2682
|
+
const typography = this.kb.getTypography(config.mode);
|
|
2265
2683
|
const docConfig = {
|
|
2266
2684
|
title: config.title,
|
|
2267
2685
|
slides: slideHtml.join("\n"),
|
|
2268
2686
|
theme: config.theme ?? "default",
|
|
2269
2687
|
revealConfig: this.defaultRevealConfig,
|
|
2270
|
-
mode: config.mode
|
|
2688
|
+
mode: config.mode,
|
|
2689
|
+
palette,
|
|
2690
|
+
typography
|
|
2271
2691
|
};
|
|
2272
2692
|
if (config.author) docConfig.author = config.author;
|
|
2273
2693
|
if (config.subject) docConfig.subject = config.subject;
|
|
@@ -2278,11 +2698,23 @@ var RevealJsGenerator = class {
|
|
|
2278
2698
|
}
|
|
2279
2699
|
return html;
|
|
2280
2700
|
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Detect presentation type from config
|
|
2703
|
+
*/
|
|
2704
|
+
detectPresentationType(config) {
|
|
2705
|
+
if (config.presentationType) {
|
|
2706
|
+
return config.presentationType;
|
|
2707
|
+
}
|
|
2708
|
+
if (config.mode === "keynote") {
|
|
2709
|
+
return "ted_keynote";
|
|
2710
|
+
}
|
|
2711
|
+
return "consulting_deck";
|
|
2712
|
+
}
|
|
2281
2713
|
/**
|
|
2282
2714
|
* Build the complete HTML document.
|
|
2283
2715
|
*/
|
|
2284
2716
|
buildDocument(options) {
|
|
2285
|
-
const { title, author, subject, slides, theme, customCSS, revealConfig, mode } = options;
|
|
2717
|
+
const { title, author, subject, slides, theme, customCSS, revealConfig, mode, palette, typography } = options;
|
|
2286
2718
|
return `<!DOCTYPE html>
|
|
2287
2719
|
<html lang="en">
|
|
2288
2720
|
<head>
|
|
@@ -2290,7 +2722,7 @@ var RevealJsGenerator = class {
|
|
|
2290
2722
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2291
2723
|
<meta name="author" content="${this.escapeHtml(author ?? "Claude Presentation Master")}">
|
|
2292
2724
|
<meta name="description" content="${this.escapeHtml(subject ?? "")}">
|
|
2293
|
-
<meta name="generator" content="Claude Presentation Master
|
|
2725
|
+
<meta name="generator" content="Claude Presentation Master v6.0.0">
|
|
2294
2726
|
<title>${this.escapeHtml(title)}</title>
|
|
2295
2727
|
|
|
2296
2728
|
<!-- Reveal.js CSS -->
|
|
@@ -2304,10 +2736,10 @@ var RevealJsGenerator = class {
|
|
|
2304
2736
|
<!-- Mermaid for diagrams -->
|
|
2305
2737
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
|
2306
2738
|
|
|
2307
|
-
<!-- Presentation Engine CSS -->
|
|
2739
|
+
<!-- Presentation Engine CSS - KB-Driven Design System -->
|
|
2308
2740
|
<style>
|
|
2309
|
-
${this.getBaseStyles(mode)}
|
|
2310
|
-
${this.getThemeStyles(theme)}
|
|
2741
|
+
${this.getBaseStyles(mode, palette, typography)}
|
|
2742
|
+
${this.getThemeStyles(theme, palette)}
|
|
2311
2743
|
${this.getAnimationStyles()}
|
|
2312
2744
|
${customCSS ?? ""}
|
|
2313
2745
|
</style>
|
|
@@ -2359,26 +2791,35 @@ ${slides}
|
|
|
2359
2791
|
</html>`;
|
|
2360
2792
|
}
|
|
2361
2793
|
/**
|
|
2362
|
-
* Get base styles for slides
|
|
2794
|
+
* Get base styles for slides - KB-DRIVEN
|
|
2363
2795
|
*/
|
|
2364
|
-
getBaseStyles(mode) {
|
|
2796
|
+
getBaseStyles(mode, palette, typography) {
|
|
2365
2797
|
const fontSize = mode === "keynote" ? "2.5em" : "1.8em";
|
|
2366
2798
|
const lineHeight = mode === "keynote" ? "1.4" : "1.5";
|
|
2799
|
+
const primary = palette.primary || "#1a1a2e";
|
|
2800
|
+
const secondary = palette.secondary || "#16213e";
|
|
2801
|
+
const accent = palette.accent || "#0f3460";
|
|
2802
|
+
const highlight = palette.accent || "#e94560";
|
|
2803
|
+
const text = palette.text || "#1a1a2e";
|
|
2804
|
+
const background = palette.background || "#ffffff";
|
|
2367
2805
|
return `
|
|
2368
|
-
/* Base Styles */
|
|
2806
|
+
/* Base Styles - KB-Driven Design System */
|
|
2369
2807
|
:root {
|
|
2808
|
+
/* Typography from KB */
|
|
2370
2809
|
--font-heading: 'Source Sans Pro', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
2371
2810
|
--font-body: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
2372
2811
|
--font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
|
|
2373
2812
|
|
|
2374
|
-
|
|
2375
|
-
--color-
|
|
2376
|
-
--color-
|
|
2377
|
-
--color-
|
|
2378
|
-
--color-
|
|
2379
|
-
--color-text
|
|
2380
|
-
--color-
|
|
2813
|
+
/* Colors from KB Palette: ${palette.name} */
|
|
2814
|
+
--color-primary: ${primary};
|
|
2815
|
+
--color-secondary: ${secondary};
|
|
2816
|
+
--color-accent: ${accent};
|
|
2817
|
+
--color-highlight: ${highlight};
|
|
2818
|
+
--color-text: ${text};
|
|
2819
|
+
--color-text-light: ${this.lightenColor(text, 30)};
|
|
2820
|
+
--color-background: ${background};
|
|
2381
2821
|
|
|
2822
|
+
/* Layout */
|
|
2382
2823
|
--slide-padding: 60px;
|
|
2383
2824
|
--content-max-width: 1200px;
|
|
2384
2825
|
}
|
|
@@ -2494,7 +2935,7 @@ ${slides}
|
|
|
2494
2935
|
font-size: 0.8em;
|
|
2495
2936
|
}
|
|
2496
2937
|
|
|
2497
|
-
/* Images */
|
|
2938
|
+
/* Images - NO RANDOM PICSUM - Only user-provided or none */
|
|
2498
2939
|
.reveal img {
|
|
2499
2940
|
max-width: 100%;
|
|
2500
2941
|
height: auto;
|
|
@@ -2600,9 +3041,25 @@ ${slides}
|
|
|
2600
3041
|
`;
|
|
2601
3042
|
}
|
|
2602
3043
|
/**
|
|
2603
|
-
*
|
|
3044
|
+
* Lighten a hex color
|
|
3045
|
+
*/
|
|
3046
|
+
lightenColor(hex, percent) {
|
|
3047
|
+
hex = hex.replace("#", "");
|
|
3048
|
+
let r = parseInt(hex.substring(0, 2), 16);
|
|
3049
|
+
let g = parseInt(hex.substring(2, 4), 16);
|
|
3050
|
+
let b = parseInt(hex.substring(4, 6), 16);
|
|
3051
|
+
r = Math.min(255, Math.floor(r + (255 - r) * (percent / 100)));
|
|
3052
|
+
g = Math.min(255, Math.floor(g + (255 - g) * (percent / 100)));
|
|
3053
|
+
b = Math.min(255, Math.floor(b + (255 - b) * (percent / 100)));
|
|
3054
|
+
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* Get theme-specific styles - KB-DRIVEN
|
|
2604
3058
|
*/
|
|
2605
|
-
getThemeStyles(theme) {
|
|
3059
|
+
getThemeStyles(theme, palette) {
|
|
3060
|
+
if (theme === "default") {
|
|
3061
|
+
return "";
|
|
3062
|
+
}
|
|
2606
3063
|
const themes = {
|
|
2607
3064
|
"default": "",
|
|
2608
3065
|
"light-corporate": `
|
|
@@ -2616,10 +3073,10 @@ ${slides}
|
|
|
2616
3073
|
`,
|
|
2617
3074
|
"modern-tech": `
|
|
2618
3075
|
:root {
|
|
2619
|
-
--color-primary: #1a1a2e;
|
|
2620
|
-
--color-secondary: #16213e;
|
|
2621
|
-
--color-accent: #0f3460;
|
|
2622
|
-
--color-highlight: #e94560;
|
|
3076
|
+
--color-primary: ${palette.primary || "#1a1a2e"};
|
|
3077
|
+
--color-secondary: ${palette.secondary || "#16213e"};
|
|
3078
|
+
--color-accent: ${palette.accent || "#0f3460"};
|
|
3079
|
+
--color-highlight: ${palette.accent || "#e94560"};
|
|
2623
3080
|
--color-background: #f8f9fa;
|
|
2624
3081
|
}
|
|
2625
3082
|
`,
|
|
@@ -3865,239 +4322,6 @@ function createDefaultImageProvider(options) {
|
|
|
3865
4322
|
return new CompositeImageProvider(providers);
|
|
3866
4323
|
}
|
|
3867
4324
|
|
|
3868
|
-
// src/knowledge/KnowledgeBase.ts
|
|
3869
|
-
import { readFileSync } from "fs";
|
|
3870
|
-
import { join, dirname } from "path";
|
|
3871
|
-
import { fileURLToPath } from "url";
|
|
3872
|
-
import * as yaml from "yaml";
|
|
3873
|
-
function getModuleDir() {
|
|
3874
|
-
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
3875
|
-
return dirname(fileURLToPath(import.meta.url));
|
|
3876
|
-
}
|
|
3877
|
-
if (typeof __dirname !== "undefined") {
|
|
3878
|
-
return __dirname;
|
|
3879
|
-
}
|
|
3880
|
-
return process.cwd();
|
|
3881
|
-
}
|
|
3882
|
-
var moduleDir = getModuleDir();
|
|
3883
|
-
var KnowledgeBase = class {
|
|
3884
|
-
data = null;
|
|
3885
|
-
loaded = false;
|
|
3886
|
-
/**
|
|
3887
|
-
* Load the knowledge base from the bundled YAML file.
|
|
3888
|
-
*/
|
|
3889
|
-
async load() {
|
|
3890
|
-
if (this.loaded) return;
|
|
3891
|
-
try {
|
|
3892
|
-
const possiblePaths = [
|
|
3893
|
-
// From dist/: go up to package root
|
|
3894
|
-
join(moduleDir, "../../assets/presentation-knowledge.yaml"),
|
|
3895
|
-
join(moduleDir, "../assets/presentation-knowledge.yaml"),
|
|
3896
|
-
join(moduleDir, "../../../assets/presentation-knowledge.yaml"),
|
|
3897
|
-
// From bundle in dist/index.js: go up one level
|
|
3898
|
-
join(moduleDir, "assets/presentation-knowledge.yaml"),
|
|
3899
|
-
// CWD-based paths for development or local installs
|
|
3900
|
-
join(process.cwd(), "assets/presentation-knowledge.yaml"),
|
|
3901
|
-
join(process.cwd(), "node_modules/claude-presentation-master/assets/presentation-knowledge.yaml")
|
|
3902
|
-
];
|
|
3903
|
-
let assetPath = "";
|
|
3904
|
-
for (const p of possiblePaths) {
|
|
3905
|
-
try {
|
|
3906
|
-
readFileSync(p);
|
|
3907
|
-
assetPath = p;
|
|
3908
|
-
break;
|
|
3909
|
-
} catch {
|
|
3910
|
-
continue;
|
|
3911
|
-
}
|
|
3912
|
-
}
|
|
3913
|
-
if (!assetPath) {
|
|
3914
|
-
throw new Error("Could not locate knowledge base file");
|
|
3915
|
-
}
|
|
3916
|
-
const content = readFileSync(assetPath, "utf-8");
|
|
3917
|
-
this.data = yaml.parse(content);
|
|
3918
|
-
this.loaded = true;
|
|
3919
|
-
console.log(`\u{1F4DA} Knowledge base loaded: v${this.data.version}`);
|
|
3920
|
-
} catch (error) {
|
|
3921
|
-
console.warn("\u26A0\uFE0F Could not load knowledge base, using defaults");
|
|
3922
|
-
this.data = this.getDefaultData();
|
|
3923
|
-
this.loaded = true;
|
|
3924
|
-
}
|
|
3925
|
-
}
|
|
3926
|
-
/**
|
|
3927
|
-
* Get expert methodology by name.
|
|
3928
|
-
*/
|
|
3929
|
-
getExpert(name) {
|
|
3930
|
-
this.ensureLoaded();
|
|
3931
|
-
return this.data?.experts?.[name];
|
|
3932
|
-
}
|
|
3933
|
-
/**
|
|
3934
|
-
* Get all expert names.
|
|
3935
|
-
*/
|
|
3936
|
-
getExpertNames() {
|
|
3937
|
-
this.ensureLoaded();
|
|
3938
|
-
return Object.keys(this.data?.experts ?? {});
|
|
3939
|
-
}
|
|
3940
|
-
/**
|
|
3941
|
-
* Get framework recommendation for audience.
|
|
3942
|
-
*/
|
|
3943
|
-
getFrameworkForAudience(audience) {
|
|
3944
|
-
this.ensureLoaded();
|
|
3945
|
-
return this.data?.frameworkSelector?.byAudience?.[audience];
|
|
3946
|
-
}
|
|
3947
|
-
/**
|
|
3948
|
-
* Get framework recommendation for goal.
|
|
3949
|
-
*/
|
|
3950
|
-
getFrameworkForGoal(goal) {
|
|
3951
|
-
this.ensureLoaded();
|
|
3952
|
-
return this.data?.frameworkSelector?.byGoal?.[goal];
|
|
3953
|
-
}
|
|
3954
|
-
/**
|
|
3955
|
-
* Get QA scoring rubric.
|
|
3956
|
-
*/
|
|
3957
|
-
getScoringRubric() {
|
|
3958
|
-
this.ensureLoaded();
|
|
3959
|
-
return this.data?.automatedQA?.scoringRubric;
|
|
3960
|
-
}
|
|
3961
|
-
/**
|
|
3962
|
-
* Get mode configuration (keynote or business).
|
|
3963
|
-
*/
|
|
3964
|
-
getModeConfig(mode) {
|
|
3965
|
-
this.ensureLoaded();
|
|
3966
|
-
return this.data?.modes?.[mode];
|
|
3967
|
-
}
|
|
3968
|
-
/**
|
|
3969
|
-
* Get slide type configuration.
|
|
3970
|
-
*/
|
|
3971
|
-
getSlideType(type) {
|
|
3972
|
-
this.ensureLoaded();
|
|
3973
|
-
return this.data?.slideTypes?.[type];
|
|
3974
|
-
}
|
|
3975
|
-
/**
|
|
3976
|
-
* Get the knowledge base version.
|
|
3977
|
-
*/
|
|
3978
|
-
getVersion() {
|
|
3979
|
-
this.ensureLoaded();
|
|
3980
|
-
return this.data?.version ?? "unknown";
|
|
3981
|
-
}
|
|
3982
|
-
/**
|
|
3983
|
-
* Validate a slide against expert principles.
|
|
3984
|
-
*/
|
|
3985
|
-
validateAgainstExpert(expertName, slideData) {
|
|
3986
|
-
const expert = this.getExpert(expertName);
|
|
3987
|
-
if (!expert) {
|
|
3988
|
-
return { passed: true, violations: [] };
|
|
3989
|
-
}
|
|
3990
|
-
const violations = [];
|
|
3991
|
-
if (expert.wordLimits) {
|
|
3992
|
-
if (expert.wordLimits.max && slideData.wordCount > expert.wordLimits.max) {
|
|
3993
|
-
violations.push(`Exceeds ${expertName} word limit of ${expert.wordLimits.max}`);
|
|
3994
|
-
}
|
|
3995
|
-
if (expert.wordLimits.min && slideData.wordCount < expert.wordLimits.min) {
|
|
3996
|
-
violations.push(`Below ${expertName} minimum of ${expert.wordLimits.min} words`);
|
|
3997
|
-
}
|
|
3998
|
-
}
|
|
3999
|
-
return {
|
|
4000
|
-
passed: violations.length === 0,
|
|
4001
|
-
violations
|
|
4002
|
-
};
|
|
4003
|
-
}
|
|
4004
|
-
/**
|
|
4005
|
-
* Ensure knowledge base is loaded.
|
|
4006
|
-
*/
|
|
4007
|
-
ensureLoaded() {
|
|
4008
|
-
if (!this.loaded) {
|
|
4009
|
-
this.data = this.getDefaultData();
|
|
4010
|
-
this.loaded = true;
|
|
4011
|
-
}
|
|
4012
|
-
}
|
|
4013
|
-
/**
|
|
4014
|
-
* Get default data if YAML can't be loaded.
|
|
4015
|
-
*/
|
|
4016
|
-
getDefaultData() {
|
|
4017
|
-
return {
|
|
4018
|
-
version: "1.0.0-fallback",
|
|
4019
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4020
|
-
experts: {
|
|
4021
|
-
"Nancy Duarte": {
|
|
4022
|
-
name: "Nancy Duarte",
|
|
4023
|
-
principles: [
|
|
4024
|
-
{ name: "Glance Test", description: "Message clear in 3 seconds" },
|
|
4025
|
-
{ name: "STAR Moment", description: "Something They'll Always Remember" },
|
|
4026
|
-
{ name: "Sparkline", description: "Contrast What Is vs What Could Be" }
|
|
4027
|
-
]
|
|
4028
|
-
},
|
|
4029
|
-
"Garr Reynolds": {
|
|
4030
|
-
name: "Garr Reynolds",
|
|
4031
|
-
principles: [
|
|
4032
|
-
{ name: "Signal-to-Noise", description: "Maximize signal, minimize noise" },
|
|
4033
|
-
{ name: "Simplicity", description: "Amplify through simplification" }
|
|
4034
|
-
]
|
|
4035
|
-
},
|
|
4036
|
-
"Carmine Gallo": {
|
|
4037
|
-
name: "Carmine Gallo",
|
|
4038
|
-
principles: [
|
|
4039
|
-
{ name: "Rule of Three", description: "Maximum 3 key messages" },
|
|
4040
|
-
{ name: "Emotional Connection", description: "Connect emotionally first" }
|
|
4041
|
-
]
|
|
4042
|
-
},
|
|
4043
|
-
"Chris Anderson": {
|
|
4044
|
-
name: "Chris Anderson",
|
|
4045
|
-
principles: [
|
|
4046
|
-
{ name: "One Idea", description: "One powerful idea per talk" },
|
|
4047
|
-
{ name: "Dead Laptop Test", description: "Present without slides" }
|
|
4048
|
-
]
|
|
4049
|
-
}
|
|
4050
|
-
},
|
|
4051
|
-
frameworkSelector: {
|
|
4052
|
-
byAudience: {
|
|
4053
|
-
"board": {
|
|
4054
|
-
primaryFramework: "Barbara Minto",
|
|
4055
|
-
slideTypes: ["executive_summary", "data_insight"]
|
|
4056
|
-
},
|
|
4057
|
-
"sales": {
|
|
4058
|
-
primaryFramework: "Nancy Duarte",
|
|
4059
|
-
slideTypes: ["big_idea", "social_proof"]
|
|
4060
|
-
}
|
|
4061
|
-
},
|
|
4062
|
-
byGoal: {
|
|
4063
|
-
"persuade": {
|
|
4064
|
-
primaryFramework: "Nancy Duarte",
|
|
4065
|
-
slideTypes: ["big_idea", "star_moment"]
|
|
4066
|
-
},
|
|
4067
|
-
"inform": {
|
|
4068
|
-
primaryFramework: "Barbara Minto",
|
|
4069
|
-
slideTypes: ["bullet_points", "data_insight"]
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
},
|
|
4073
|
-
automatedQA: {
|
|
4074
|
-
scoringRubric: {
|
|
4075
|
-
totalPoints: 100,
|
|
4076
|
-
passingThreshold: 95,
|
|
4077
|
-
categories: {
|
|
4078
|
-
visual: { weight: 35, checks: {} },
|
|
4079
|
-
content: { weight: 30, checks: {} },
|
|
4080
|
-
expert: { weight: 25, checks: {} },
|
|
4081
|
-
accessibility: { weight: 10, checks: {} }
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
},
|
|
4085
|
-
slideTypes: {},
|
|
4086
|
-
modes: {
|
|
4087
|
-
keynote: { maxWords: 25, minWhitespace: 35 },
|
|
4088
|
-
business: { maxWords: 80, minWhitespace: 25 }
|
|
4089
|
-
}
|
|
4090
|
-
};
|
|
4091
|
-
}
|
|
4092
|
-
};
|
|
4093
|
-
var knowledgeBaseInstance = null;
|
|
4094
|
-
function getKnowledgeBase() {
|
|
4095
|
-
if (!knowledgeBaseInstance) {
|
|
4096
|
-
knowledgeBaseInstance = new KnowledgeBase();
|
|
4097
|
-
}
|
|
4098
|
-
return knowledgeBaseInstance;
|
|
4099
|
-
}
|
|
4100
|
-
|
|
4101
4325
|
// src/index.ts
|
|
4102
4326
|
async function generate(config) {
|
|
4103
4327
|
const engine = new PresentationEngine();
|
|
@@ -4112,7 +4336,7 @@ async function validate(presentation, options) {
|
|
|
4112
4336
|
score
|
|
4113
4337
|
};
|
|
4114
4338
|
}
|
|
4115
|
-
var VERSION = "
|
|
4339
|
+
var VERSION = "6.0.0";
|
|
4116
4340
|
var index_default = {
|
|
4117
4341
|
generate,
|
|
4118
4342
|
validate,
|
|
@@ -4125,7 +4349,7 @@ export {
|
|
|
4125
4349
|
CompositeChartProvider,
|
|
4126
4350
|
CompositeImageProvider,
|
|
4127
4351
|
ContentAnalyzer,
|
|
4128
|
-
|
|
4352
|
+
KnowledgeGateway,
|
|
4129
4353
|
LocalImageProvider,
|
|
4130
4354
|
MermaidProvider,
|
|
4131
4355
|
PlaceholderImageProvider,
|
|
@@ -4146,7 +4370,7 @@ export {
|
|
|
4146
4370
|
createDefaultImageProvider,
|
|
4147
4371
|
index_default as default,
|
|
4148
4372
|
generate,
|
|
4149
|
-
|
|
4373
|
+
getKnowledgeGateway,
|
|
4150
4374
|
validate
|
|
4151
4375
|
};
|
|
4152
4376
|
/**
|