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/bin/cli.js +2 -2
- package/dist/index.d.mts +307 -158
- package/dist/index.d.ts +307 -158
- package/dist/index.js +829 -615
- package/dist/index.mjs +826 -612
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
"
|
|
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
|
-
//
|
|
154
|
-
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"
|
|
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
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
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
|
-
|
|
197
|
-
|
|
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
|
|
489
|
+
* Parse content based on type
|
|
206
490
|
*/
|
|
207
491
|
parseContent(content, contentType) {
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return
|
|
243
|
-
}
|
|
244
|
-
|
|
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
|
-
*
|
|
512
|
+
* Detect presentation type from content signals
|
|
249
513
|
*/
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
|
540
|
+
return detectedType;
|
|
260
541
|
}
|
|
261
542
|
/**
|
|
262
|
-
*
|
|
543
|
+
* Extract sections from content (headers, bullets, content)
|
|
263
544
|
*/
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
274
|
-
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return
|
|
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
|
|
612
|
+
* Extract SCQA structure (Barbara Minto)
|
|
298
613
|
*/
|
|
299
|
-
extractSCQA(
|
|
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.
|
|
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.
|
|
627
|
+
if (this.containsSignals(para.toLowerCase(), this.complicationSignals)) {
|
|
628
|
+
complication = this.extractFirstSentence(para);
|
|
313
629
|
break;
|
|
314
630
|
}
|
|
315
631
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
323
|
-
|
|
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 (!
|
|
329
|
-
|
|
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 (
|
|
332
|
-
|
|
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
|
|
659
|
+
* Extract STAR moments (Something They'll Always Remember)
|
|
660
|
+
* Per Nancy Duarte - these are emotional peaks in the presentation
|
|
339
661
|
*/
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
350
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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 {
|
|
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
|
|
704
|
+
extractKeyMessages(text) {
|
|
365
705
|
const messages = [];
|
|
366
|
-
const
|
|
367
|
-
if (
|
|
368
|
-
for (const match of
|
|
369
|
-
const
|
|
370
|
-
if (
|
|
371
|
-
messages.push(
|
|
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
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
*
|
|
730
|
+
* Extract data points (metrics with values)
|
|
402
731
|
*/
|
|
403
|
-
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
|
756
|
+
return dataPoints.slice(0, 6);
|
|
422
757
|
}
|
|
423
758
|
/**
|
|
424
|
-
*
|
|
759
|
+
* Get context around a match
|
|
425
760
|
*/
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
*
|
|
769
|
+
* Extract a label from surrounding context
|
|
770
|
+
* Fixes the "Century Interactive |" garbage issue by stripping table syntax
|
|
448
771
|
*/
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const
|
|
520
|
-
const
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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 <=
|
|
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
|
-
},
|
|
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.
|
|
2120
|
-
reynolds: this.
|
|
2121
|
-
gallo: this.
|
|
2122
|
-
anderson: this.
|
|
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
|
-
|
|
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:
|
|
2128
|
-
principlesChecked:
|
|
2444
|
+
expertName: "Nancy Duarte",
|
|
2445
|
+
principlesChecked: ["Glance Test", "STAR Moment", "Sparkline Structure"],
|
|
2129
2446
|
passed: score >= 80,
|
|
2130
|
-
score,
|
|
2131
|
-
violations
|
|
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
|
|
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
|
-
|
|
2447
|
-
--color-
|
|
2448
|
-
--color-
|
|
2449
|
-
--color-
|
|
2450
|
-
--color-
|
|
2451
|
-
--color-text
|
|
2452
|
-
--color-
|
|
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
|
-
*
|
|
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 = "
|
|
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
|
-
|
|
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
|
-
|
|
4436
|
+
getKnowledgeGateway,
|
|
4223
4437
|
validate
|
|
4224
4438
|
});
|
|
4225
4439
|
/**
|