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.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
|
-
} catch {
|
|
244
|
-
return content;
|
|
500
|
+
extractTitle(text) {
|
|
501
|
+
const h1Match = text.match(/^#\s+(.+)$/m);
|
|
502
|
+
if (h1Match && h1Match[1]) {
|
|
503
|
+
return h1Match[1].replace(/\*\*/g, "").trim();
|
|
245
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);
|
|
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
|
+
}
|
|
257
530
|
}
|
|
258
531
|
}
|
|
259
|
-
|
|
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;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
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;
|
|
272
569
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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;
|
|
598
|
+
}
|
|
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"]
|
|
@@ -866,14 +1145,22 @@ var SlideFactory = class {
|
|
|
866
1145
|
return templates;
|
|
867
1146
|
}
|
|
868
1147
|
// === Helper Methods ===
|
|
1148
|
+
/**
|
|
1149
|
+
* Clean text by removing all content markers.
|
|
1150
|
+
*/
|
|
1151
|
+
cleanText(text) {
|
|
1152
|
+
if (!text) return "";
|
|
1153
|
+
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();
|
|
1154
|
+
}
|
|
869
1155
|
/**
|
|
870
1156
|
* Truncate text to max length at word boundary.
|
|
871
1157
|
*/
|
|
872
1158
|
truncate(text, maxLength) {
|
|
873
|
-
|
|
874
|
-
|
|
1159
|
+
const cleanedText = this.cleanText(text);
|
|
1160
|
+
if (!cleanedText || cleanedText.length <= maxLength) {
|
|
1161
|
+
return cleanedText;
|
|
875
1162
|
}
|
|
876
|
-
const truncated =
|
|
1163
|
+
const truncated = cleanedText.slice(0, maxLength);
|
|
877
1164
|
const lastSpace = truncated.lastIndexOf(" ");
|
|
878
1165
|
if (lastSpace > maxLength * 0.7) {
|
|
879
1166
|
return truncated.slice(0, lastSpace) + "...";
|
|
@@ -884,11 +1171,12 @@ var SlideFactory = class {
|
|
|
884
1171
|
* Extract an action title from a message.
|
|
885
1172
|
*/
|
|
886
1173
|
extractActionTitle(message) {
|
|
887
|
-
const
|
|
1174
|
+
const cleanedMessage = this.cleanText(message);
|
|
1175
|
+
const firstSentence = cleanedMessage.split(/[.!?]/)[0];
|
|
888
1176
|
if (firstSentence && firstSentence.length <= 50) {
|
|
889
1177
|
return firstSentence;
|
|
890
1178
|
}
|
|
891
|
-
const words =
|
|
1179
|
+
const words = cleanedMessage.split(/\s+/).slice(0, 6);
|
|
892
1180
|
return words.join(" ");
|
|
893
1181
|
}
|
|
894
1182
|
/**
|
|
@@ -898,9 +1186,10 @@ var SlideFactory = class {
|
|
|
898
1186
|
if (!text) return [];
|
|
899
1187
|
const bulletMatches = text.match(/\[BULLET\]\s*(.+)/g);
|
|
900
1188
|
if (bulletMatches && bulletMatches.length > 0) {
|
|
901
|
-
return bulletMatches.map((b) =>
|
|
1189
|
+
return bulletMatches.map((b) => this.cleanText(b)).slice(0, 5);
|
|
902
1190
|
}
|
|
903
|
-
const
|
|
1191
|
+
const cleanedText = this.cleanText(text);
|
|
1192
|
+
const sentences = cleanedText.split(/[.!?]+/).filter((s) => s.trim().length > 10);
|
|
904
1193
|
return sentences.slice(0, 5).map((s) => s.trim());
|
|
905
1194
|
}
|
|
906
1195
|
/**
|
|
@@ -1807,19 +2096,28 @@ var ScoreCalculator = class {
|
|
|
1807
2096
|
var import_playwright = require("playwright");
|
|
1808
2097
|
var QAEngine = class {
|
|
1809
2098
|
browser = null;
|
|
2099
|
+
kb;
|
|
1810
2100
|
/**
|
|
1811
|
-
*
|
|
2101
|
+
* Initialize KnowledgeGateway
|
|
2102
|
+
*/
|
|
2103
|
+
async initialize() {
|
|
2104
|
+
this.kb = await getKnowledgeGateway();
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Validate a presentation using KB-driven quality metrics
|
|
1812
2108
|
*/
|
|
1813
2109
|
async validate(presentation, options) {
|
|
2110
|
+
await this.initialize();
|
|
1814
2111
|
const html = typeof presentation === "string" ? presentation : presentation.toString("utf-8");
|
|
1815
2112
|
const mode = options?.mode ?? "keynote";
|
|
2113
|
+
const qualityMetrics = this.kb.getQualityMetrics();
|
|
1816
2114
|
await this.initBrowser();
|
|
1817
2115
|
try {
|
|
1818
2116
|
const [visualResults, contentResults, expertResults, accessibilityResults] = await Promise.all([
|
|
1819
|
-
this.runVisualTests(html, mode),
|
|
1820
|
-
this.runContentTests(html, mode),
|
|
2117
|
+
this.runVisualTests(html, mode, qualityMetrics),
|
|
2118
|
+
this.runContentTests(html, mode, qualityMetrics),
|
|
1821
2119
|
this.runExpertTests(html, mode),
|
|
1822
|
-
this.runAccessibilityTests(html)
|
|
2120
|
+
this.runAccessibilityTests(html, qualityMetrics)
|
|
1823
2121
|
]);
|
|
1824
2122
|
const issues = this.collectIssues(visualResults, contentResults, expertResults, accessibilityResults);
|
|
1825
2123
|
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
@@ -1837,18 +2135,18 @@ var QAEngine = class {
|
|
|
1837
2135
|
}
|
|
1838
2136
|
}
|
|
1839
2137
|
/**
|
|
1840
|
-
* Calculate overall QA score
|
|
2138
|
+
* Calculate overall QA score using KB-driven weights
|
|
1841
2139
|
*/
|
|
1842
2140
|
calculateScore(results) {
|
|
1843
2141
|
const weights = {
|
|
1844
2142
|
visual: 0.35,
|
|
1845
|
-
// 35%
|
|
2143
|
+
// 35% - Design quality
|
|
1846
2144
|
content: 0.3,
|
|
1847
|
-
// 30%
|
|
2145
|
+
// 30% - Content quality
|
|
1848
2146
|
expert: 0.25,
|
|
1849
|
-
// 25%
|
|
2147
|
+
// 25% - Expert methodology
|
|
1850
2148
|
accessibility: 0.1
|
|
1851
|
-
// 10%
|
|
2149
|
+
// 10% - Accessibility
|
|
1852
2150
|
};
|
|
1853
2151
|
const visualScore = this.calculateVisualScore(results.visual);
|
|
1854
2152
|
const contentScore = this.calculateContentScore(results.content);
|
|
@@ -1895,13 +2193,15 @@ var QAEngine = class {
|
|
|
1895
2193
|
};
|
|
1896
2194
|
}
|
|
1897
2195
|
// ===========================================================================
|
|
1898
|
-
// VISUAL TESTS
|
|
2196
|
+
// VISUAL TESTS - KB-DRIVEN
|
|
1899
2197
|
// ===========================================================================
|
|
1900
|
-
async runVisualTests(html, mode) {
|
|
2198
|
+
async runVisualTests(html, mode, qualityMetrics) {
|
|
1901
2199
|
const page = await this.browser.newPage();
|
|
1902
2200
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
1903
2201
|
await page.setContent(html);
|
|
1904
2202
|
await page.waitForTimeout(1e3);
|
|
2203
|
+
const modeMetrics = mode === "keynote" ? qualityMetrics.keynote_mode : qualityMetrics.business_mode;
|
|
2204
|
+
const minWhitespace = this.kb.getWhitespaceRequirement(mode) * 100;
|
|
1905
2205
|
const slideCount = await page.evaluate(() => {
|
|
1906
2206
|
return window.Reveal?.getTotalSlides?.() ?? document.querySelectorAll(".slides > section").length;
|
|
1907
2207
|
});
|
|
@@ -1967,9 +2267,8 @@ var QAEngine = class {
|
|
|
1967
2267
|
}, { slideIndex: i });
|
|
1968
2268
|
if (slideAnalysis) {
|
|
1969
2269
|
const issues = [];
|
|
1970
|
-
const minWhitespace = mode === "keynote" ? 40 : 25;
|
|
1971
2270
|
if (slideAnalysis.whitespace < minWhitespace) {
|
|
1972
|
-
issues.push(`Whitespace ${slideAnalysis.whitespace}% below ${minWhitespace}% minimum`);
|
|
2271
|
+
issues.push(`Whitespace ${slideAnalysis.whitespace}% below ${minWhitespace}% minimum (KB-defined)`);
|
|
1973
2272
|
}
|
|
1974
2273
|
if (slideAnalysis.whitespace > 80) {
|
|
1975
2274
|
issues.push(`Whitespace ${slideAnalysis.whitespace}% - slide appears sparse`);
|
|
@@ -2024,34 +2323,35 @@ var QAEngine = class {
|
|
|
2024
2323
|
};
|
|
2025
2324
|
}
|
|
2026
2325
|
// ===========================================================================
|
|
2027
|
-
// CONTENT TESTS
|
|
2326
|
+
// CONTENT TESTS - KB-DRIVEN
|
|
2028
2327
|
// ===========================================================================
|
|
2029
|
-
async runContentTests(html, mode) {
|
|
2328
|
+
async runContentTests(html, mode, qualityMetrics) {
|
|
2030
2329
|
const page = await this.browser.newPage();
|
|
2031
2330
|
await page.setContent(html);
|
|
2032
2331
|
await page.waitForTimeout(500);
|
|
2033
|
-
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;
|
|
2034
2336
|
const slides = document.querySelectorAll(".slides > section");
|
|
2035
2337
|
const perSlide = [];
|
|
2036
|
-
const
|
|
2338
|
+
const glanceTestResults = [];
|
|
2037
2339
|
const signalNoise = [];
|
|
2038
2340
|
const oneIdea = [];
|
|
2039
2341
|
slides.forEach((slide, index) => {
|
|
2040
2342
|
const text = slide.innerText || "";
|
|
2041
2343
|
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
2042
2344
|
const wordCount = words.length;
|
|
2043
|
-
const maxWords = targetMode === "keynote" ? 25 : 80;
|
|
2044
|
-
const minWords = targetMode === "business" ? 20 : 0;
|
|
2045
2345
|
const withinLimit = wordCount <= maxWords && wordCount >= minWords;
|
|
2046
2346
|
const title = slide.querySelector("h2")?.textContent || "";
|
|
2047
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);
|
|
2048
2348
|
const hasInsight = title.length > 30 && hasVerb;
|
|
2049
2349
|
const issues = [];
|
|
2050
2350
|
if (!withinLimit) {
|
|
2051
|
-
issues.push(`Word count ${wordCount} outside ${minWords}-${maxWords} range`);
|
|
2351
|
+
issues.push(`Word count ${wordCount} outside ${minWords}-${maxWords} range (KB-defined)`);
|
|
2052
2352
|
}
|
|
2053
2353
|
if (targetMode === "business" && !hasInsight && index > 0) {
|
|
2054
|
-
issues.push("Title is not action-oriented (missing insight)");
|
|
2354
|
+
issues.push("Title is not action-oriented (missing insight per Minto)");
|
|
2055
2355
|
}
|
|
2056
2356
|
perSlide.push({
|
|
2057
2357
|
slideIndex: index,
|
|
@@ -2064,12 +2364,12 @@ var QAEngine = class {
|
|
|
2064
2364
|
const keyMessage = prominentElement?.textContent?.trim() || "";
|
|
2065
2365
|
const keyWordCount = keyMessage.split(/\s+/).filter((w) => w.length > 0).length;
|
|
2066
2366
|
const readingTime = keyWordCount / 4.2;
|
|
2067
|
-
|
|
2367
|
+
glanceTestResults.push({
|
|
2068
2368
|
slideIndex: index,
|
|
2069
2369
|
keyMessage,
|
|
2070
2370
|
wordCount: keyWordCount,
|
|
2071
2371
|
readingTime: Math.round(readingTime * 10) / 10,
|
|
2072
|
-
passed: readingTime <= 3 && keyWordCount <=
|
|
2372
|
+
passed: readingTime <= 3 && keyWordCount <= glanceWordLimit,
|
|
2073
2373
|
recommendation: readingTime > 3 ? `Shorten to ${Math.floor(3 * 4.2)} words or less` : void 0
|
|
2074
2374
|
});
|
|
2075
2375
|
const elements = slide.querySelectorAll("h1, h2, h3, p, li, img");
|
|
@@ -2096,35 +2396,143 @@ var QAEngine = class {
|
|
|
2096
2396
|
conflictingIdeas: ideaCount > 2 ? ["Multiple competing ideas detected"] : void 0
|
|
2097
2397
|
});
|
|
2098
2398
|
});
|
|
2099
|
-
return { perSlide, glanceTest, signalNoise, oneIdea };
|
|
2100
|
-
},
|
|
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
|
+
});
|
|
2101
2406
|
await page.close();
|
|
2102
2407
|
return results;
|
|
2103
2408
|
}
|
|
2104
2409
|
// ===========================================================================
|
|
2105
|
-
// EXPERT TESTS
|
|
2410
|
+
// EXPERT TESTS - KB-DRIVEN
|
|
2106
2411
|
// ===========================================================================
|
|
2107
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");
|
|
2417
|
+
return {
|
|
2418
|
+
duarte: this.validateDuartePrinciples(html, duartePrinciples),
|
|
2419
|
+
reynolds: this.validateReynoldsPrinciples(html, reynoldsPrinciples),
|
|
2420
|
+
gallo: this.validateGalloPrinciples(html, galloPrinciples),
|
|
2421
|
+
anderson: this.validateAndersonPrinciples(html, andersonPrinciples)
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
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
|
+
}
|
|
2108
2443
|
return {
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2444
|
+
expertName: "Nancy Duarte",
|
|
2445
|
+
principlesChecked: ["Glance Test", "STAR Moment", "Sparkline Structure"],
|
|
2446
|
+
passed: score >= 80,
|
|
2447
|
+
score: Math.max(0, score),
|
|
2448
|
+
violations
|
|
2113
2449
|
};
|
|
2114
2450
|
}
|
|
2115
|
-
|
|
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
|
+
}
|
|
2116
2470
|
return {
|
|
2117
|
-
expertName:
|
|
2118
|
-
principlesChecked:
|
|
2471
|
+
expertName: "Garr Reynolds",
|
|
2472
|
+
principlesChecked: ["Signal-to-Noise", "Simplicity", "Picture Superiority"],
|
|
2119
2473
|
passed: score >= 80,
|
|
2120
|
-
score,
|
|
2121
|
-
violations
|
|
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
|
|
2122
2530
|
};
|
|
2123
2531
|
}
|
|
2124
2532
|
// ===========================================================================
|
|
2125
|
-
// ACCESSIBILITY TESTS
|
|
2533
|
+
// ACCESSIBILITY TESTS - KB-DRIVEN
|
|
2126
2534
|
// ===========================================================================
|
|
2127
|
-
async runAccessibilityTests(html) {
|
|
2535
|
+
async runAccessibilityTests(html, qualityMetrics) {
|
|
2128
2536
|
const page = await this.browser.newPage();
|
|
2129
2537
|
await page.setContent(html);
|
|
2130
2538
|
await page.waitForTimeout(500);
|
|
@@ -2174,7 +2582,7 @@ var QAEngine = class {
|
|
|
2174
2582
|
};
|
|
2175
2583
|
}
|
|
2176
2584
|
// ===========================================================================
|
|
2177
|
-
// SCORING
|
|
2585
|
+
// SCORING - KB-DRIVEN
|
|
2178
2586
|
// ===========================================================================
|
|
2179
2587
|
calculateVisualScore(results) {
|
|
2180
2588
|
let score = 100;
|
|
@@ -2298,6 +2706,7 @@ var QAEngine = class {
|
|
|
2298
2706
|
// src/generators/html/RevealJsGenerator.ts
|
|
2299
2707
|
var RevealJsGenerator = class {
|
|
2300
2708
|
templateEngine;
|
|
2709
|
+
kb;
|
|
2301
2710
|
defaultRevealConfig = {
|
|
2302
2711
|
revealVersion: "5.0.4",
|
|
2303
2712
|
hash: true,
|
|
@@ -2316,20 +2725,32 @@ var RevealJsGenerator = class {
|
|
|
2316
2725
|
constructor() {
|
|
2317
2726
|
this.templateEngine = new TemplateEngine();
|
|
2318
2727
|
}
|
|
2728
|
+
/**
|
|
2729
|
+
* Initialize KnowledgeGateway
|
|
2730
|
+
*/
|
|
2731
|
+
async initialize() {
|
|
2732
|
+
this.kb = await getKnowledgeGateway();
|
|
2733
|
+
}
|
|
2319
2734
|
/**
|
|
2320
2735
|
* Generate complete Reveal.js HTML presentation.
|
|
2321
2736
|
*/
|
|
2322
2737
|
async generate(slides, config) {
|
|
2738
|
+
await this.initialize();
|
|
2323
2739
|
const templateConfig = {};
|
|
2324
2740
|
if (config.theme) templateConfig.theme = config.theme;
|
|
2325
2741
|
if (config.customTemplates) templateConfig.customTemplates = config.customTemplates;
|
|
2326
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);
|
|
2327
2746
|
const docConfig = {
|
|
2328
2747
|
title: config.title,
|
|
2329
2748
|
slides: slideHtml.join("\n"),
|
|
2330
2749
|
theme: config.theme ?? "default",
|
|
2331
2750
|
revealConfig: this.defaultRevealConfig,
|
|
2332
|
-
mode: config.mode
|
|
2751
|
+
mode: config.mode,
|
|
2752
|
+
palette,
|
|
2753
|
+
typography
|
|
2333
2754
|
};
|
|
2334
2755
|
if (config.author) docConfig.author = config.author;
|
|
2335
2756
|
if (config.subject) docConfig.subject = config.subject;
|
|
@@ -2340,11 +2761,23 @@ var RevealJsGenerator = class {
|
|
|
2340
2761
|
}
|
|
2341
2762
|
return html;
|
|
2342
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
|
+
}
|
|
2343
2776
|
/**
|
|
2344
2777
|
* Build the complete HTML document.
|
|
2345
2778
|
*/
|
|
2346
2779
|
buildDocument(options) {
|
|
2347
|
-
const { title, author, subject, slides, theme, customCSS, revealConfig, mode } = options;
|
|
2780
|
+
const { title, author, subject, slides, theme, customCSS, revealConfig, mode, palette, typography } = options;
|
|
2348
2781
|
return `<!DOCTYPE html>
|
|
2349
2782
|
<html lang="en">
|
|
2350
2783
|
<head>
|
|
@@ -2352,7 +2785,7 @@ var RevealJsGenerator = class {
|
|
|
2352
2785
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2353
2786
|
<meta name="author" content="${this.escapeHtml(author ?? "Claude Presentation Master")}">
|
|
2354
2787
|
<meta name="description" content="${this.escapeHtml(subject ?? "")}">
|
|
2355
|
-
<meta name="generator" content="Claude Presentation Master
|
|
2788
|
+
<meta name="generator" content="Claude Presentation Master v6.0.0">
|
|
2356
2789
|
<title>${this.escapeHtml(title)}</title>
|
|
2357
2790
|
|
|
2358
2791
|
<!-- Reveal.js CSS -->
|
|
@@ -2366,10 +2799,10 @@ var RevealJsGenerator = class {
|
|
|
2366
2799
|
<!-- Mermaid for diagrams -->
|
|
2367
2800
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
|
|
2368
2801
|
|
|
2369
|
-
<!-- Presentation Engine CSS -->
|
|
2802
|
+
<!-- Presentation Engine CSS - KB-Driven Design System -->
|
|
2370
2803
|
<style>
|
|
2371
|
-
${this.getBaseStyles(mode)}
|
|
2372
|
-
${this.getThemeStyles(theme)}
|
|
2804
|
+
${this.getBaseStyles(mode, palette, typography)}
|
|
2805
|
+
${this.getThemeStyles(theme, palette)}
|
|
2373
2806
|
${this.getAnimationStyles()}
|
|
2374
2807
|
${customCSS ?? ""}
|
|
2375
2808
|
</style>
|
|
@@ -2421,26 +2854,35 @@ ${slides}
|
|
|
2421
2854
|
</html>`;
|
|
2422
2855
|
}
|
|
2423
2856
|
/**
|
|
2424
|
-
* Get base styles for slides
|
|
2857
|
+
* Get base styles for slides - KB-DRIVEN
|
|
2425
2858
|
*/
|
|
2426
|
-
getBaseStyles(mode) {
|
|
2859
|
+
getBaseStyles(mode, palette, typography) {
|
|
2427
2860
|
const fontSize = mode === "keynote" ? "2.5em" : "1.8em";
|
|
2428
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";
|
|
2429
2868
|
return `
|
|
2430
|
-
/* Base Styles */
|
|
2869
|
+
/* Base Styles - KB-Driven Design System */
|
|
2431
2870
|
:root {
|
|
2871
|
+
/* Typography from KB */
|
|
2432
2872
|
--font-heading: 'Source Sans Pro', 'Helvetica Neue', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
2433
2873
|
--font-body: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
2434
2874
|
--font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace;
|
|
2435
2875
|
|
|
2436
|
-
|
|
2437
|
-
--color-
|
|
2438
|
-
--color-
|
|
2439
|
-
--color-
|
|
2440
|
-
--color-
|
|
2441
|
-
--color-text
|
|
2442
|
-
--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};
|
|
2443
2884
|
|
|
2885
|
+
/* Layout */
|
|
2444
2886
|
--slide-padding: 60px;
|
|
2445
2887
|
--content-max-width: 1200px;
|
|
2446
2888
|
}
|
|
@@ -2556,7 +2998,7 @@ ${slides}
|
|
|
2556
2998
|
font-size: 0.8em;
|
|
2557
2999
|
}
|
|
2558
3000
|
|
|
2559
|
-
/* Images */
|
|
3001
|
+
/* Images - NO RANDOM PICSUM - Only user-provided or none */
|
|
2560
3002
|
.reveal img {
|
|
2561
3003
|
max-width: 100%;
|
|
2562
3004
|
height: auto;
|
|
@@ -2662,9 +3104,25 @@ ${slides}
|
|
|
2662
3104
|
`;
|
|
2663
3105
|
}
|
|
2664
3106
|
/**
|
|
2665
|
-
*
|
|
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
|
|
2666
3121
|
*/
|
|
2667
|
-
getThemeStyles(theme) {
|
|
3122
|
+
getThemeStyles(theme, palette) {
|
|
3123
|
+
if (theme === "default") {
|
|
3124
|
+
return "";
|
|
3125
|
+
}
|
|
2668
3126
|
const themes = {
|
|
2669
3127
|
"default": "",
|
|
2670
3128
|
"light-corporate": `
|
|
@@ -2678,10 +3136,10 @@ ${slides}
|
|
|
2678
3136
|
`,
|
|
2679
3137
|
"modern-tech": `
|
|
2680
3138
|
:root {
|
|
2681
|
-
--color-primary: #1a1a2e;
|
|
2682
|
-
--color-secondary: #16213e;
|
|
2683
|
-
--color-accent: #0f3460;
|
|
2684
|
-
--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"};
|
|
2685
3143
|
--color-background: #f8f9fa;
|
|
2686
3144
|
}
|
|
2687
3145
|
`,
|
|
@@ -3927,240 +4385,6 @@ function createDefaultImageProvider(options) {
|
|
|
3927
4385
|
return new CompositeImageProvider(providers);
|
|
3928
4386
|
}
|
|
3929
4387
|
|
|
3930
|
-
// src/knowledge/KnowledgeBase.ts
|
|
3931
|
-
var import_fs = require("fs");
|
|
3932
|
-
var import_path = require("path");
|
|
3933
|
-
var import_url = require("url");
|
|
3934
|
-
var yaml = __toESM(require("yaml"));
|
|
3935
|
-
var import_meta = {};
|
|
3936
|
-
function getModuleDir() {
|
|
3937
|
-
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
3938
|
-
return (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
3939
|
-
}
|
|
3940
|
-
if (typeof __dirname !== "undefined") {
|
|
3941
|
-
return __dirname;
|
|
3942
|
-
}
|
|
3943
|
-
return process.cwd();
|
|
3944
|
-
}
|
|
3945
|
-
var moduleDir = getModuleDir();
|
|
3946
|
-
var KnowledgeBase = class {
|
|
3947
|
-
data = null;
|
|
3948
|
-
loaded = false;
|
|
3949
|
-
/**
|
|
3950
|
-
* Load the knowledge base from the bundled YAML file.
|
|
3951
|
-
*/
|
|
3952
|
-
async load() {
|
|
3953
|
-
if (this.loaded) return;
|
|
3954
|
-
try {
|
|
3955
|
-
const possiblePaths = [
|
|
3956
|
-
// From dist/: go up to package root
|
|
3957
|
-
(0, import_path.join)(moduleDir, "../../assets/presentation-knowledge.yaml"),
|
|
3958
|
-
(0, import_path.join)(moduleDir, "../assets/presentation-knowledge.yaml"),
|
|
3959
|
-
(0, import_path.join)(moduleDir, "../../../assets/presentation-knowledge.yaml"),
|
|
3960
|
-
// From bundle in dist/index.js: go up one level
|
|
3961
|
-
(0, import_path.join)(moduleDir, "assets/presentation-knowledge.yaml"),
|
|
3962
|
-
// CWD-based paths for development or local installs
|
|
3963
|
-
(0, import_path.join)(process.cwd(), "assets/presentation-knowledge.yaml"),
|
|
3964
|
-
(0, import_path.join)(process.cwd(), "node_modules/claude-presentation-master/assets/presentation-knowledge.yaml")
|
|
3965
|
-
];
|
|
3966
|
-
let assetPath = "";
|
|
3967
|
-
for (const p of possiblePaths) {
|
|
3968
|
-
try {
|
|
3969
|
-
(0, import_fs.readFileSync)(p);
|
|
3970
|
-
assetPath = p;
|
|
3971
|
-
break;
|
|
3972
|
-
} catch {
|
|
3973
|
-
continue;
|
|
3974
|
-
}
|
|
3975
|
-
}
|
|
3976
|
-
if (!assetPath) {
|
|
3977
|
-
throw new Error("Could not locate knowledge base file");
|
|
3978
|
-
}
|
|
3979
|
-
const content = (0, import_fs.readFileSync)(assetPath, "utf-8");
|
|
3980
|
-
this.data = yaml.parse(content);
|
|
3981
|
-
this.loaded = true;
|
|
3982
|
-
console.log(`\u{1F4DA} Knowledge base loaded: v${this.data.version}`);
|
|
3983
|
-
} catch (error) {
|
|
3984
|
-
console.warn("\u26A0\uFE0F Could not load knowledge base, using defaults");
|
|
3985
|
-
this.data = this.getDefaultData();
|
|
3986
|
-
this.loaded = true;
|
|
3987
|
-
}
|
|
3988
|
-
}
|
|
3989
|
-
/**
|
|
3990
|
-
* Get expert methodology by name.
|
|
3991
|
-
*/
|
|
3992
|
-
getExpert(name) {
|
|
3993
|
-
this.ensureLoaded();
|
|
3994
|
-
return this.data?.experts?.[name];
|
|
3995
|
-
}
|
|
3996
|
-
/**
|
|
3997
|
-
* Get all expert names.
|
|
3998
|
-
*/
|
|
3999
|
-
getExpertNames() {
|
|
4000
|
-
this.ensureLoaded();
|
|
4001
|
-
return Object.keys(this.data?.experts ?? {});
|
|
4002
|
-
}
|
|
4003
|
-
/**
|
|
4004
|
-
* Get framework recommendation for audience.
|
|
4005
|
-
*/
|
|
4006
|
-
getFrameworkForAudience(audience) {
|
|
4007
|
-
this.ensureLoaded();
|
|
4008
|
-
return this.data?.frameworkSelector?.byAudience?.[audience];
|
|
4009
|
-
}
|
|
4010
|
-
/**
|
|
4011
|
-
* Get framework recommendation for goal.
|
|
4012
|
-
*/
|
|
4013
|
-
getFrameworkForGoal(goal) {
|
|
4014
|
-
this.ensureLoaded();
|
|
4015
|
-
return this.data?.frameworkSelector?.byGoal?.[goal];
|
|
4016
|
-
}
|
|
4017
|
-
/**
|
|
4018
|
-
* Get QA scoring rubric.
|
|
4019
|
-
*/
|
|
4020
|
-
getScoringRubric() {
|
|
4021
|
-
this.ensureLoaded();
|
|
4022
|
-
return this.data?.automatedQA?.scoringRubric;
|
|
4023
|
-
}
|
|
4024
|
-
/**
|
|
4025
|
-
* Get mode configuration (keynote or business).
|
|
4026
|
-
*/
|
|
4027
|
-
getModeConfig(mode) {
|
|
4028
|
-
this.ensureLoaded();
|
|
4029
|
-
return this.data?.modes?.[mode];
|
|
4030
|
-
}
|
|
4031
|
-
/**
|
|
4032
|
-
* Get slide type configuration.
|
|
4033
|
-
*/
|
|
4034
|
-
getSlideType(type) {
|
|
4035
|
-
this.ensureLoaded();
|
|
4036
|
-
return this.data?.slideTypes?.[type];
|
|
4037
|
-
}
|
|
4038
|
-
/**
|
|
4039
|
-
* Get the knowledge base version.
|
|
4040
|
-
*/
|
|
4041
|
-
getVersion() {
|
|
4042
|
-
this.ensureLoaded();
|
|
4043
|
-
return this.data?.version ?? "unknown";
|
|
4044
|
-
}
|
|
4045
|
-
/**
|
|
4046
|
-
* Validate a slide against expert principles.
|
|
4047
|
-
*/
|
|
4048
|
-
validateAgainstExpert(expertName, slideData) {
|
|
4049
|
-
const expert = this.getExpert(expertName);
|
|
4050
|
-
if (!expert) {
|
|
4051
|
-
return { passed: true, violations: [] };
|
|
4052
|
-
}
|
|
4053
|
-
const violations = [];
|
|
4054
|
-
if (expert.wordLimits) {
|
|
4055
|
-
if (expert.wordLimits.max && slideData.wordCount > expert.wordLimits.max) {
|
|
4056
|
-
violations.push(`Exceeds ${expertName} word limit of ${expert.wordLimits.max}`);
|
|
4057
|
-
}
|
|
4058
|
-
if (expert.wordLimits.min && slideData.wordCount < expert.wordLimits.min) {
|
|
4059
|
-
violations.push(`Below ${expertName} minimum of ${expert.wordLimits.min} words`);
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
return {
|
|
4063
|
-
passed: violations.length === 0,
|
|
4064
|
-
violations
|
|
4065
|
-
};
|
|
4066
|
-
}
|
|
4067
|
-
/**
|
|
4068
|
-
* Ensure knowledge base is loaded.
|
|
4069
|
-
*/
|
|
4070
|
-
ensureLoaded() {
|
|
4071
|
-
if (!this.loaded) {
|
|
4072
|
-
this.data = this.getDefaultData();
|
|
4073
|
-
this.loaded = true;
|
|
4074
|
-
}
|
|
4075
|
-
}
|
|
4076
|
-
/**
|
|
4077
|
-
* Get default data if YAML can't be loaded.
|
|
4078
|
-
*/
|
|
4079
|
-
getDefaultData() {
|
|
4080
|
-
return {
|
|
4081
|
-
version: "1.0.0-fallback",
|
|
4082
|
-
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4083
|
-
experts: {
|
|
4084
|
-
"Nancy Duarte": {
|
|
4085
|
-
name: "Nancy Duarte",
|
|
4086
|
-
principles: [
|
|
4087
|
-
{ name: "Glance Test", description: "Message clear in 3 seconds" },
|
|
4088
|
-
{ name: "STAR Moment", description: "Something They'll Always Remember" },
|
|
4089
|
-
{ name: "Sparkline", description: "Contrast What Is vs What Could Be" }
|
|
4090
|
-
]
|
|
4091
|
-
},
|
|
4092
|
-
"Garr Reynolds": {
|
|
4093
|
-
name: "Garr Reynolds",
|
|
4094
|
-
principles: [
|
|
4095
|
-
{ name: "Signal-to-Noise", description: "Maximize signal, minimize noise" },
|
|
4096
|
-
{ name: "Simplicity", description: "Amplify through simplification" }
|
|
4097
|
-
]
|
|
4098
|
-
},
|
|
4099
|
-
"Carmine Gallo": {
|
|
4100
|
-
name: "Carmine Gallo",
|
|
4101
|
-
principles: [
|
|
4102
|
-
{ name: "Rule of Three", description: "Maximum 3 key messages" },
|
|
4103
|
-
{ name: "Emotional Connection", description: "Connect emotionally first" }
|
|
4104
|
-
]
|
|
4105
|
-
},
|
|
4106
|
-
"Chris Anderson": {
|
|
4107
|
-
name: "Chris Anderson",
|
|
4108
|
-
principles: [
|
|
4109
|
-
{ name: "One Idea", description: "One powerful idea per talk" },
|
|
4110
|
-
{ name: "Dead Laptop Test", description: "Present without slides" }
|
|
4111
|
-
]
|
|
4112
|
-
}
|
|
4113
|
-
},
|
|
4114
|
-
frameworkSelector: {
|
|
4115
|
-
byAudience: {
|
|
4116
|
-
"board": {
|
|
4117
|
-
primaryFramework: "Barbara Minto",
|
|
4118
|
-
slideTypes: ["executive_summary", "data_insight"]
|
|
4119
|
-
},
|
|
4120
|
-
"sales": {
|
|
4121
|
-
primaryFramework: "Nancy Duarte",
|
|
4122
|
-
slideTypes: ["big_idea", "social_proof"]
|
|
4123
|
-
}
|
|
4124
|
-
},
|
|
4125
|
-
byGoal: {
|
|
4126
|
-
"persuade": {
|
|
4127
|
-
primaryFramework: "Nancy Duarte",
|
|
4128
|
-
slideTypes: ["big_idea", "star_moment"]
|
|
4129
|
-
},
|
|
4130
|
-
"inform": {
|
|
4131
|
-
primaryFramework: "Barbara Minto",
|
|
4132
|
-
slideTypes: ["bullet_points", "data_insight"]
|
|
4133
|
-
}
|
|
4134
|
-
}
|
|
4135
|
-
},
|
|
4136
|
-
automatedQA: {
|
|
4137
|
-
scoringRubric: {
|
|
4138
|
-
totalPoints: 100,
|
|
4139
|
-
passingThreshold: 95,
|
|
4140
|
-
categories: {
|
|
4141
|
-
visual: { weight: 35, checks: {} },
|
|
4142
|
-
content: { weight: 30, checks: {} },
|
|
4143
|
-
expert: { weight: 25, checks: {} },
|
|
4144
|
-
accessibility: { weight: 10, checks: {} }
|
|
4145
|
-
}
|
|
4146
|
-
}
|
|
4147
|
-
},
|
|
4148
|
-
slideTypes: {},
|
|
4149
|
-
modes: {
|
|
4150
|
-
keynote: { maxWords: 25, minWhitespace: 35 },
|
|
4151
|
-
business: { maxWords: 80, minWhitespace: 25 }
|
|
4152
|
-
}
|
|
4153
|
-
};
|
|
4154
|
-
}
|
|
4155
|
-
};
|
|
4156
|
-
var knowledgeBaseInstance = null;
|
|
4157
|
-
function getKnowledgeBase() {
|
|
4158
|
-
if (!knowledgeBaseInstance) {
|
|
4159
|
-
knowledgeBaseInstance = new KnowledgeBase();
|
|
4160
|
-
}
|
|
4161
|
-
return knowledgeBaseInstance;
|
|
4162
|
-
}
|
|
4163
|
-
|
|
4164
4388
|
// src/index.ts
|
|
4165
4389
|
async function generate(config) {
|
|
4166
4390
|
const engine = new PresentationEngine();
|
|
@@ -4175,7 +4399,7 @@ async function validate(presentation, options) {
|
|
|
4175
4399
|
score
|
|
4176
4400
|
};
|
|
4177
4401
|
}
|
|
4178
|
-
var VERSION = "
|
|
4402
|
+
var VERSION = "6.0.0";
|
|
4179
4403
|
var index_default = {
|
|
4180
4404
|
generate,
|
|
4181
4405
|
validate,
|
|
@@ -4189,7 +4413,7 @@ var index_default = {
|
|
|
4189
4413
|
CompositeChartProvider,
|
|
4190
4414
|
CompositeImageProvider,
|
|
4191
4415
|
ContentAnalyzer,
|
|
4192
|
-
|
|
4416
|
+
KnowledgeGateway,
|
|
4193
4417
|
LocalImageProvider,
|
|
4194
4418
|
MermaidProvider,
|
|
4195
4419
|
PlaceholderImageProvider,
|
|
@@ -4209,7 +4433,7 @@ var index_default = {
|
|
|
4209
4433
|
createDefaultChartProvider,
|
|
4210
4434
|
createDefaultImageProvider,
|
|
4211
4435
|
generate,
|
|
4212
|
-
|
|
4436
|
+
getKnowledgeGateway,
|
|
4213
4437
|
validate
|
|
4214
4438
|
});
|
|
4215
4439
|
/**
|