claude-presentation-master 8.0.0 → 8.1.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 +70 -8
- package/dist/index.d.mts +45 -9
- package/dist/index.d.ts +45 -9
- package/dist/index.js +430 -147
- package/dist/index.mjs +430 -147
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -778,7 +778,8 @@ var KnowledgeGateway = class {
|
|
|
778
778
|
// Subtitle needs more room for complete phrases - at least 10 words for keynote
|
|
779
779
|
subtitle: { maxWords: mode === "keynote" ? 12 : Math.min(20, Math.floor(maxWords / 2)) },
|
|
780
780
|
context: { maxWords: Math.min(30, Math.floor(maxWords / 2)) },
|
|
781
|
-
|
|
781
|
+
// Step descriptions need minimum 8 words for meaningful content
|
|
782
|
+
step: { maxWords: Math.max(8, Math.min(20, Math.floor(maxWords / 3))) },
|
|
782
783
|
columnContent: { maxWords: Math.min(25, Math.floor(maxWords / 3)) }
|
|
783
784
|
};
|
|
784
785
|
}
|
|
@@ -2826,9 +2827,8 @@ var SlideFactory = class {
|
|
|
2826
2827
|
const steps = this.classifier.extractSteps(section);
|
|
2827
2828
|
const maxSteps = Math.min(
|
|
2828
2829
|
steps.length,
|
|
2829
|
-
this.config.rules.bulletsPerSlide.max,
|
|
2830
2830
|
this.config.millersLaw.maxItems
|
|
2831
|
-
// FROM KB - 7±2 rule
|
|
2831
|
+
// FROM KB - 7±2 rule (max 9)
|
|
2832
2832
|
);
|
|
2833
2833
|
return {
|
|
2834
2834
|
index,
|
|
@@ -2850,7 +2850,7 @@ var SlideFactory = class {
|
|
|
2850
2850
|
}
|
|
2851
2851
|
createProcessSlide(index, section) {
|
|
2852
2852
|
const steps = this.classifier.extractSteps(section);
|
|
2853
|
-
const maxSteps = Math.min(steps.length, this.config.
|
|
2853
|
+
const maxSteps = Math.min(steps.length, this.config.millersLaw.maxItems);
|
|
2854
2854
|
return {
|
|
2855
2855
|
index,
|
|
2856
2856
|
type: "process",
|
|
@@ -5257,169 +5257,422 @@ var VisualQualityEvaluator = class {
|
|
|
5257
5257
|
const title = currentSlide.querySelector("h1, h2, .title")?.textContent?.trim() || "";
|
|
5258
5258
|
const body = currentSlide.querySelector(".body, p:not(.subtitle)")?.textContent?.trim() || "";
|
|
5259
5259
|
const bullets = Array.from(currentSlide.querySelectorAll("li")).map((li) => li.textContent?.trim() || "");
|
|
5260
|
+
const hasSteps = !!currentSlide.querySelector(".steps, .process-steps, .timeline");
|
|
5261
|
+
const steps = Array.from(currentSlide.querySelectorAll(".step, .process-step, .timeline-item")).map(
|
|
5262
|
+
(s) => s.textContent?.trim() || ""
|
|
5263
|
+
);
|
|
5264
|
+
const hasMetrics = !!currentSlide.querySelector(".metrics, .metric");
|
|
5260
5265
|
const hasImage = !!currentSlide.querySelector("img");
|
|
5261
5266
|
const hasChart = !!currentSlide.querySelector(".chart, svg, canvas");
|
|
5262
5267
|
const classList = Array.from(currentSlide.classList);
|
|
5263
5268
|
const backgroundColor = window.getComputedStyle(currentSlide).backgroundColor;
|
|
5264
5269
|
const titleEl = currentSlide.querySelector("h1, h2, .title");
|
|
5265
5270
|
const titleStyles = titleEl ? window.getComputedStyle(titleEl) : null;
|
|
5271
|
+
const truncatedElements = [];
|
|
5272
|
+
const contentElements = currentSlide.querySelectorAll("h1, h2, h3, p, span, li, .body, .step-desc, .step-title, .timeline-content");
|
|
5273
|
+
contentElements.forEach((el, idx) => {
|
|
5274
|
+
const styles = window.getComputedStyle(el);
|
|
5275
|
+
const text = el.textContent?.trim() || "";
|
|
5276
|
+
if (text.length < 15) return;
|
|
5277
|
+
const isLayoutContainer = el.classList?.contains("slide-content") || el.classList?.contains("steps") || el.classList?.contains("timeline") || el.classList?.contains("process-steps");
|
|
5278
|
+
if (isLayoutContainer) return;
|
|
5279
|
+
if (styles.textOverflow === "ellipsis") {
|
|
5280
|
+
if (el.scrollWidth > el.clientWidth + 5) {
|
|
5281
|
+
truncatedElements.push(`Element ${idx}: "${text.substring(0, 30)}..." is truncated horizontally`);
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
const scrollHeight = el.scrollHeight;
|
|
5285
|
+
const clientHeight = el.clientHeight;
|
|
5286
|
+
if (scrollHeight > clientHeight + 20) {
|
|
5287
|
+
const overflow = styles.overflow || styles.overflowY;
|
|
5288
|
+
if (overflow === "hidden" || overflow === "clip") {
|
|
5289
|
+
truncatedElements.push(`Element ${idx}: "${text.substring(0, 30)}..." is truncated vertically`);
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
});
|
|
5293
|
+
const allVisibleText = Array.from(currentSlide.querySelectorAll("*")).map((el) => el.textContent?.trim() || "").join(" ").trim();
|
|
5294
|
+
const isEmptySlide = allVisibleText.length < 10 && !hasImage && !hasChart;
|
|
5295
|
+
const hasOnlyTitle = title.length > 0 && body.length === 0 && bullets.length === 0 && steps.length === 0 && !hasSteps && !hasMetrics && !hasImage && !hasChart;
|
|
5296
|
+
const titleLower = title.toLowerCase();
|
|
5297
|
+
const bodyLower = body.toLowerCase();
|
|
5298
|
+
const isRedundant = titleLower.length > 10 && bodyLower.length > 10 && (titleLower.includes(bodyLower) || bodyLower.includes(titleLower));
|
|
5299
|
+
const contrastIssues = [];
|
|
5300
|
+
const parseRGB = (color) => {
|
|
5301
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
5302
|
+
if (match && match[1] !== void 0 && match[2] !== void 0 && match[3] !== void 0) {
|
|
5303
|
+
return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) };
|
|
5304
|
+
}
|
|
5305
|
+
return null;
|
|
5306
|
+
};
|
|
5307
|
+
const getLuminance = (rgb) => {
|
|
5308
|
+
const values = [rgb.r, rgb.g, rgb.b].map((c) => {
|
|
5309
|
+
c = c / 255;
|
|
5310
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
5311
|
+
});
|
|
5312
|
+
return 0.2126 * (values[0] ?? 0) + 0.7152 * (values[1] ?? 0) + 0.0722 * (values[2] ?? 0);
|
|
5313
|
+
};
|
|
5314
|
+
const getContrastRatio = (fg, bg) => {
|
|
5315
|
+
const l1 = getLuminance(fg);
|
|
5316
|
+
const l2 = getLuminance(bg);
|
|
5317
|
+
const lighter = Math.max(l1, l2);
|
|
5318
|
+
const darker = Math.min(l1, l2);
|
|
5319
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
5320
|
+
};
|
|
5321
|
+
const bgRGB = parseRGB(backgroundColor);
|
|
5322
|
+
if (titleEl && bgRGB) {
|
|
5323
|
+
const titleRGB = parseRGB(titleStyles?.color || "");
|
|
5324
|
+
if (titleRGB) {
|
|
5325
|
+
const contrast = getContrastRatio(titleRGB, bgRGB);
|
|
5326
|
+
if (contrast < 4.5) {
|
|
5327
|
+
contrastIssues.push(`Title contrast ${contrast.toFixed(1)}:1 (need 4.5:1)`);
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5330
|
+
}
|
|
5331
|
+
const bodyEl = currentSlide.querySelector(".body, p:not(.subtitle)");
|
|
5332
|
+
if (bodyEl && bgRGB) {
|
|
5333
|
+
const bodyStyles2 = window.getComputedStyle(bodyEl);
|
|
5334
|
+
const bodyRGB = parseRGB(bodyStyles2.color);
|
|
5335
|
+
if (bodyRGB) {
|
|
5336
|
+
const contrast = getContrastRatio(bodyRGB, bgRGB);
|
|
5337
|
+
if (contrast < 4.5) {
|
|
5338
|
+
contrastIssues.push(`Body contrast ${contrast.toFixed(1)}:1 (need 4.5:1)`);
|
|
5339
|
+
}
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
const hasContrastIssues = contrastIssues.length > 0;
|
|
5343
|
+
const layoutIssues = [];
|
|
5344
|
+
const slideRect = currentSlide.getBoundingClientRect();
|
|
5345
|
+
const layoutElements = currentSlide.querySelectorAll("h1, h2, h3, p, ul, ol, .number, .metric, img, canvas, svg");
|
|
5346
|
+
let topHeavy = 0;
|
|
5347
|
+
let bottomHeavy = 0;
|
|
5348
|
+
let leftHeavy = 0;
|
|
5349
|
+
let rightHeavy = 0;
|
|
5350
|
+
let centerY = slideRect.height / 2;
|
|
5351
|
+
let centerX = slideRect.width / 2;
|
|
5352
|
+
layoutElements.forEach((el) => {
|
|
5353
|
+
const rect = el.getBoundingClientRect();
|
|
5354
|
+
const elCenterY = rect.top + rect.height / 2 - slideRect.top;
|
|
5355
|
+
const elCenterX = rect.left + rect.width / 2 - slideRect.left;
|
|
5356
|
+
if (elCenterY < centerY) topHeavy++;
|
|
5357
|
+
else bottomHeavy++;
|
|
5358
|
+
if (elCenterX < centerX) leftHeavy++;
|
|
5359
|
+
else rightHeavy++;
|
|
5360
|
+
});
|
|
5361
|
+
const verticalBalance = Math.abs(topHeavy - bottomHeavy) / Math.max(1, topHeavy + bottomHeavy);
|
|
5362
|
+
const horizontalBalance = Math.abs(leftHeavy - rightHeavy) / Math.max(1, leftHeavy + rightHeavy);
|
|
5363
|
+
if (verticalBalance > 0.6 && contentElements.length > 2) {
|
|
5364
|
+
layoutIssues.push(`Vertical imbalance: ${topHeavy} top vs ${bottomHeavy} bottom`);
|
|
5365
|
+
}
|
|
5366
|
+
if (horizontalBalance > 0.6 && contentElements.length > 2) {
|
|
5367
|
+
layoutIssues.push(`Horizontal imbalance: ${leftHeavy} left vs ${rightHeavy} right`);
|
|
5368
|
+
}
|
|
5369
|
+
const hasLayoutIssues = layoutIssues.length > 0;
|
|
5370
|
+
const typographyIssues = [];
|
|
5371
|
+
const titleSize = parseFloat(titleStyles?.fontSize || "0");
|
|
5372
|
+
const bodyStyles = bodyEl ? window.getComputedStyle(bodyEl) : null;
|
|
5373
|
+
const bodySize = parseFloat(bodyStyles?.fontSize || "0");
|
|
5374
|
+
if (titleSize > 0 && bodySize > 0 && titleSize <= bodySize) {
|
|
5375
|
+
typographyIssues.push("Title not larger than body - hierarchy broken");
|
|
5376
|
+
}
|
|
5377
|
+
if (titleSize > 0 && bodySize > 0 && titleSize < bodySize * 1.3) {
|
|
5378
|
+
typographyIssues.push("Title not prominent enough vs body");
|
|
5379
|
+
}
|
|
5380
|
+
const hasTypographyIssues = typographyIssues.length > 0;
|
|
5381
|
+
const completenessIssues = [];
|
|
5382
|
+
const emptySteps = currentSlide.querySelectorAll(".steps:empty, .timeline:empty, .process-steps:empty, .steps > :empty");
|
|
5383
|
+
if (emptySteps.length > 0) {
|
|
5384
|
+
completenessIssues.push(`${emptySteps.length} empty step/timeline container(s) - content missing`);
|
|
5385
|
+
}
|
|
5386
|
+
if (body.includes("Lorem") || body.includes("placeholder") || body.includes("TODO")) {
|
|
5387
|
+
completenessIssues.push("Contains placeholder text");
|
|
5388
|
+
}
|
|
5389
|
+
const emptyBullets = bullets.filter((b) => b.length < 3).length;
|
|
5390
|
+
if (emptyBullets > 0 && bullets.length > 0) {
|
|
5391
|
+
completenessIssues.push(`${emptyBullets} empty/minimal bullets`);
|
|
5392
|
+
}
|
|
5393
|
+
const hasCompletenessIssues = completenessIssues.length > 0;
|
|
5394
|
+
const visualNeedsIssues = [];
|
|
5395
|
+
const visualRequiredTypes = ["data", "chart", "metrics", "comparison", "process", "timeline"];
|
|
5396
|
+
const visualRecommendedTypes = ["big-number", "quote", "testimonial"];
|
|
5397
|
+
const slideClasses = Array.from(currentSlide.classList).join(" ").toLowerCase();
|
|
5398
|
+
const needsVisual = visualRequiredTypes.some((t) => slideClasses.includes(t));
|
|
5399
|
+
const visualRecommended = visualRecommendedTypes.some((t) => slideClasses.includes(t));
|
|
5400
|
+
const contentText = (title + " " + body).toLowerCase();
|
|
5401
|
+
const dataIndicators = /\d+%|\$[\d,]+|\d+x|million|billion|percent|growth|increase|decrease|comparison|versus|vs\b|before|after/i;
|
|
5402
|
+
const hasDataContent = dataIndicators.test(contentText);
|
|
5403
|
+
const hasAnyVisual = hasImage || hasChart;
|
|
5404
|
+
if (needsVisual && !hasAnyVisual) {
|
|
5405
|
+
visualNeedsIssues.push("Data/process slide missing visual - needs chart, diagram, or image");
|
|
5406
|
+
} else if (hasDataContent && !hasAnyVisual && bullets.length === 0) {
|
|
5407
|
+
visualNeedsIssues.push("Content has numbers/data but no visualization - chart would help");
|
|
5408
|
+
} else if (visualRecommended && !hasAnyVisual) {
|
|
5409
|
+
visualNeedsIssues.push("Visual recommended for this slide type");
|
|
5410
|
+
}
|
|
5411
|
+
if (hasImage) {
|
|
5412
|
+
const img = currentSlide.querySelector("img");
|
|
5413
|
+
const imgSrc = img?.getAttribute("src") || "";
|
|
5414
|
+
if (imgSrc.includes("placeholder") || imgSrc.includes("picsum") || imgSrc.includes("via.placeholder")) {
|
|
5415
|
+
visualNeedsIssues.push("Placeholder image detected - needs real visual");
|
|
5416
|
+
}
|
|
5417
|
+
}
|
|
5418
|
+
const needsVisualButMissing = visualNeedsIssues.length > 0;
|
|
5419
|
+
const visualScore = hasAnyVisual ? 10 : needsVisual || hasDataContent ? 0 : 5;
|
|
5266
5420
|
return {
|
|
5267
5421
|
title,
|
|
5268
5422
|
body,
|
|
5269
5423
|
bullets,
|
|
5424
|
+
steps,
|
|
5425
|
+
// NEW: Timeline/process step content
|
|
5426
|
+
hasSteps,
|
|
5427
|
+
// NEW: Has .steps/.process-steps container
|
|
5428
|
+
hasMetrics,
|
|
5429
|
+
// NEW: Has metrics content
|
|
5270
5430
|
hasImage,
|
|
5271
5431
|
hasChart,
|
|
5272
5432
|
classList,
|
|
5273
5433
|
backgroundColor,
|
|
5274
5434
|
titleFontSize: titleStyles?.fontSize || "",
|
|
5275
5435
|
titleColor: titleStyles?.color || "",
|
|
5276
|
-
|
|
5436
|
+
// Include steps in content length calculation
|
|
5437
|
+
contentLength: (title + body + bullets.join(" ") + steps.join(" ")).length,
|
|
5438
|
+
// Visual quality flags
|
|
5439
|
+
hasTruncatedText: truncatedElements.length > 0,
|
|
5440
|
+
truncatedElements,
|
|
5441
|
+
isEmptySlide,
|
|
5442
|
+
hasOnlyTitle,
|
|
5443
|
+
isRedundant,
|
|
5444
|
+
allVisibleTextLength: allVisibleText.length,
|
|
5445
|
+
// NEW: Expert quality checks
|
|
5446
|
+
hasContrastIssues,
|
|
5447
|
+
contrastIssues,
|
|
5448
|
+
hasLayoutIssues,
|
|
5449
|
+
layoutIssues,
|
|
5450
|
+
hasTypographyIssues,
|
|
5451
|
+
typographyIssues,
|
|
5452
|
+
hasCompletenessIssues,
|
|
5453
|
+
completenessIssues,
|
|
5454
|
+
// Visual needs analysis
|
|
5455
|
+
needsVisualButMissing,
|
|
5456
|
+
visualNeedsIssues,
|
|
5457
|
+
visualScore
|
|
5277
5458
|
};
|
|
5278
5459
|
});
|
|
5279
5460
|
return this.scoreSlide(slideIndex, slideData, screenshotPath);
|
|
5280
5461
|
}
|
|
5462
|
+
/**
|
|
5463
|
+
* EXPERT-LEVEL SLIDE SCORING
|
|
5464
|
+
*
|
|
5465
|
+
* This evaluates each slide like Nancy Duarte, Carmine Gallo, or a McKinsey partner would.
|
|
5466
|
+
* It's not about rules - it's about whether the slide WORKS.
|
|
5467
|
+
*/
|
|
5281
5468
|
scoreSlide(slideIndex, slideData, screenshotPath) {
|
|
5282
5469
|
if (!slideData) {
|
|
5283
|
-
return
|
|
5284
|
-
slideIndex,
|
|
5285
|
-
slideType: "unknown",
|
|
5286
|
-
visualImpact: 0,
|
|
5287
|
-
visualImpactNotes: "Could not analyze slide",
|
|
5288
|
-
contentClarity: 0,
|
|
5289
|
-
contentClarityNotes: "Could not analyze slide",
|
|
5290
|
-
professionalPolish: 0,
|
|
5291
|
-
professionalPolishNotes: "Could not analyze slide",
|
|
5292
|
-
themeCoherence: 0,
|
|
5293
|
-
themeCoherenceNotes: "Could not analyze slide",
|
|
5294
|
-
totalScore: 0,
|
|
5295
|
-
screenshotPath
|
|
5296
|
-
};
|
|
5470
|
+
return this.createFailedSlideScore(slideIndex, "unknown", "Could not analyze slide", screenshotPath);
|
|
5297
5471
|
}
|
|
5298
5472
|
const slideType = this.inferSlideType(slideData);
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
visualImpact += 2;
|
|
5303
|
-
visualNotes.push("Has imagery");
|
|
5473
|
+
const criticalFailures = [];
|
|
5474
|
+
if (slideData.isEmptySlide) {
|
|
5475
|
+
criticalFailures.push("EMPTY SLIDE: No meaningful content - slide serves no purpose");
|
|
5304
5476
|
}
|
|
5305
|
-
if (slideData.
|
|
5306
|
-
|
|
5307
|
-
visualNotes.push("Has data visualization");
|
|
5477
|
+
if (slideData.hasOnlyTitle && !["title", "agenda", "thank-you", "cta"].includes(slideType)) {
|
|
5478
|
+
criticalFailures.push("INCOMPLETE: Slide has title but no body content - looks unfinished");
|
|
5308
5479
|
}
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
"big_number",
|
|
5312
|
-
"metrics-grid",
|
|
5313
|
-
"metrics_grid",
|
|
5314
|
-
"three-column",
|
|
5315
|
-
"three_column",
|
|
5316
|
-
"three-points",
|
|
5317
|
-
"three_points",
|
|
5318
|
-
"title-impact",
|
|
5319
|
-
"title_impact",
|
|
5320
|
-
"cta",
|
|
5321
|
-
"call-to-action",
|
|
5322
|
-
"comparison",
|
|
5323
|
-
"timeline",
|
|
5324
|
-
"process",
|
|
5325
|
-
"quote",
|
|
5326
|
-
"testimonial"
|
|
5327
|
-
];
|
|
5328
|
-
if (highImpactTypes.some((t) => slideType.includes(t.replace(/_/g, "-")) || slideType.includes(t.replace(/-/g, "_")))) {
|
|
5329
|
-
visualImpact += 2;
|
|
5330
|
-
visualNotes.push("High-impact slide type");
|
|
5480
|
+
if (slideData.hasTruncatedText) {
|
|
5481
|
+
criticalFailures.push(`TRUNCATED TEXT: ${slideData.truncatedElements.length} element(s) cut off - message incomplete`);
|
|
5331
5482
|
}
|
|
5332
|
-
if (
|
|
5333
|
-
|
|
5334
|
-
visualNotes.push("Clean single statement");
|
|
5483
|
+
if (slideData.isRedundant) {
|
|
5484
|
+
criticalFailures.push("REDUNDANT: Title and body say the same thing - violates one-idea rule");
|
|
5335
5485
|
}
|
|
5336
|
-
if (
|
|
5337
|
-
|
|
5338
|
-
|
|
5486
|
+
if (slideData.hasCompletenessIssues) {
|
|
5487
|
+
slideData.completenessIssues.forEach((issue) => {
|
|
5488
|
+
if (issue.includes("empty")) {
|
|
5489
|
+
criticalFailures.push(`INCOMPLETE: ${issue}`);
|
|
5490
|
+
}
|
|
5491
|
+
});
|
|
5339
5492
|
}
|
|
5340
|
-
if (
|
|
5341
|
-
|
|
5342
|
-
|
|
5493
|
+
if (criticalFailures.length > 0) {
|
|
5494
|
+
return this.createFailedSlideScore(slideIndex, slideType, criticalFailures.join("; "), screenshotPath, criticalFailures);
|
|
5495
|
+
}
|
|
5496
|
+
let glanceTest = 8;
|
|
5497
|
+
const glanceNotes = [];
|
|
5498
|
+
const wordCount = slideData.contentLength / 6;
|
|
5499
|
+
if (wordCount > 40) {
|
|
5500
|
+
glanceTest = 3;
|
|
5501
|
+
glanceNotes.push("Too much text - fails glance test");
|
|
5502
|
+
} else if (wordCount > 25) {
|
|
5503
|
+
glanceTest -= 3;
|
|
5504
|
+
glanceNotes.push("Text-heavy - borderline glance test");
|
|
5505
|
+
} else if (wordCount < 15) {
|
|
5506
|
+
glanceTest += 1;
|
|
5507
|
+
glanceNotes.push("Clean, minimal text - passes glance test easily");
|
|
5343
5508
|
}
|
|
5344
5509
|
if (slideData.bullets.length > 5) {
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
}
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
if (slideData.
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
if (
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
}
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5510
|
+
glanceTest -= 4;
|
|
5511
|
+
glanceNotes.push("Too many bullets - cannot scan in 3 seconds");
|
|
5512
|
+
} else if (slideData.bullets.length > 3) {
|
|
5513
|
+
glanceTest -= 2;
|
|
5514
|
+
glanceNotes.push("Multiple bullets - needs careful structuring");
|
|
5515
|
+
}
|
|
5516
|
+
if (slideData.hasImage || slideData.hasChart) {
|
|
5517
|
+
glanceTest += 1;
|
|
5518
|
+
glanceNotes.push("Visual element aids quick comprehension");
|
|
5519
|
+
}
|
|
5520
|
+
glanceTest = Math.max(0, Math.min(10, glanceTest));
|
|
5521
|
+
let oneIdea = 7;
|
|
5522
|
+
const oneIdeaNotes = [];
|
|
5523
|
+
if (slideData.title.length > 0 && slideData.title.length < 60) {
|
|
5524
|
+
oneIdea += 1;
|
|
5525
|
+
oneIdeaNotes.push("Clear, focused title");
|
|
5526
|
+
} else if (slideData.title.length > 80) {
|
|
5527
|
+
oneIdea -= 2;
|
|
5528
|
+
oneIdeaNotes.push("Title too long - multiple ideas?");
|
|
5529
|
+
} else if (slideData.title.length === 0) {
|
|
5530
|
+
oneIdea -= 4;
|
|
5531
|
+
oneIdeaNotes.push("No title - what is the one idea?");
|
|
5532
|
+
}
|
|
5533
|
+
const focusedTypes = ["big-number", "single-statement", "quote", "cta", "title"];
|
|
5534
|
+
if (focusedTypes.some((t) => slideType.includes(t))) {
|
|
5535
|
+
oneIdea += 2;
|
|
5536
|
+
oneIdeaNotes.push("Slide type naturally focuses on one idea");
|
|
5537
|
+
}
|
|
5538
|
+
if (slideData.bullets.length > 4) {
|
|
5539
|
+
oneIdea -= 3;
|
|
5540
|
+
oneIdeaNotes.push("Multiple bullets dilute the message");
|
|
5541
|
+
}
|
|
5542
|
+
oneIdea = Math.max(0, Math.min(10, oneIdea));
|
|
5543
|
+
let dataInkRatio = 7;
|
|
5544
|
+
const dataInkNotes = [];
|
|
5545
|
+
const structuredTypes = ["big-number", "metrics-grid", "three-column", "timeline", "process", "comparison"];
|
|
5546
|
+
if (structuredTypes.some((t) => slideType.includes(t))) {
|
|
5547
|
+
dataInkRatio += 1;
|
|
5548
|
+
dataInkNotes.push("Structured layout");
|
|
5549
|
+
}
|
|
5550
|
+
if (slideData.hasChart) {
|
|
5551
|
+
dataInkRatio += 2;
|
|
5552
|
+
dataInkNotes.push("Data visualization present - excellent");
|
|
5553
|
+
}
|
|
5554
|
+
if (slideData.hasImage) {
|
|
5555
|
+
dataInkRatio += 1;
|
|
5556
|
+
dataInkNotes.push("Supporting visual present");
|
|
5557
|
+
}
|
|
5558
|
+
if (slideData.needsVisualButMissing) {
|
|
5559
|
+
dataInkRatio -= 3;
|
|
5560
|
+
slideData.visualNeedsIssues.forEach((issue) => {
|
|
5561
|
+
dataInkNotes.push(`Visual: ${issue}`);
|
|
5562
|
+
});
|
|
5563
|
+
} else if (slideData.visualScore === 10) {
|
|
5564
|
+
dataInkNotes.push("Appropriate visuals for content type");
|
|
5565
|
+
}
|
|
5566
|
+
if (slideData.contentLength > 400) {
|
|
5567
|
+
dataInkRatio -= 4;
|
|
5568
|
+
dataInkNotes.push("Excessive text - low information density");
|
|
5569
|
+
}
|
|
5570
|
+
dataInkRatio = Math.max(0, Math.min(10, dataInkRatio));
|
|
5571
|
+
let professionalExecution = 7;
|
|
5572
|
+
const executionNotes = [];
|
|
5371
5573
|
const titleFontSize = parseFloat(slideData.titleFontSize || "0");
|
|
5372
5574
|
if (titleFontSize >= 40) {
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
} else if (titleFontSize < 24) {
|
|
5376
|
-
|
|
5377
|
-
|
|
5575
|
+
professionalExecution += 1;
|
|
5576
|
+
executionNotes.push("Strong title typography");
|
|
5577
|
+
} else if (titleFontSize > 0 && titleFontSize < 24) {
|
|
5578
|
+
professionalExecution -= 1;
|
|
5579
|
+
executionNotes.push("Title could be more prominent");
|
|
5378
5580
|
}
|
|
5379
5581
|
if (slideData.contentLength > 10 && slideData.contentLength < 200) {
|
|
5380
|
-
|
|
5381
|
-
|
|
5582
|
+
professionalExecution += 1;
|
|
5583
|
+
executionNotes.push("Well-balanced content density");
|
|
5382
5584
|
}
|
|
5383
|
-
const polishedTypes = [
|
|
5384
|
-
"big-number",
|
|
5385
|
-
"metrics-grid",
|
|
5386
|
-
"three-column",
|
|
5387
|
-
"three-points",
|
|
5388
|
-
"comparison",
|
|
5389
|
-
"timeline",
|
|
5390
|
-
"process",
|
|
5391
|
-
"cta",
|
|
5392
|
-
"title",
|
|
5393
|
-
"thank-you"
|
|
5394
|
-
];
|
|
5395
|
-
if (polishedTypes.some((t) => slideType.includes(t))) {
|
|
5396
|
-
professionalPolish += 1;
|
|
5397
|
-
polishNotes.push("Well-structured layout");
|
|
5398
|
-
}
|
|
5399
|
-
professionalPolish = Math.max(0, Math.min(10, professionalPolish));
|
|
5400
|
-
let themeCoherence = 7;
|
|
5401
|
-
const coherenceNotes = [];
|
|
5402
5585
|
if (slideData.classList.some((c) => c.includes("slide-"))) {
|
|
5403
|
-
|
|
5404
|
-
|
|
5586
|
+
professionalExecution += 1;
|
|
5587
|
+
executionNotes.push("Consistent with design system");
|
|
5588
|
+
}
|
|
5589
|
+
if (slideData.hasContrastIssues) {
|
|
5590
|
+
professionalExecution -= 3;
|
|
5591
|
+
slideData.contrastIssues.forEach((issue) => {
|
|
5592
|
+
executionNotes.push(`Contrast: ${issue}`);
|
|
5593
|
+
});
|
|
5594
|
+
} else {
|
|
5595
|
+
executionNotes.push("Good text contrast");
|
|
5405
5596
|
}
|
|
5406
|
-
|
|
5407
|
-
|
|
5597
|
+
if (slideData.hasLayoutIssues) {
|
|
5598
|
+
professionalExecution -= 2;
|
|
5599
|
+
slideData.layoutIssues.forEach((issue) => {
|
|
5600
|
+
executionNotes.push(`Layout: ${issue}`);
|
|
5601
|
+
});
|
|
5602
|
+
} else if (slideData.contentLength > 50) {
|
|
5603
|
+
executionNotes.push("Balanced layout");
|
|
5604
|
+
}
|
|
5605
|
+
if (slideData.hasTypographyIssues) {
|
|
5606
|
+
professionalExecution -= 2;
|
|
5607
|
+
slideData.typographyIssues.forEach((issue) => {
|
|
5608
|
+
executionNotes.push(`Typography: ${issue}`);
|
|
5609
|
+
});
|
|
5610
|
+
} else if (titleFontSize > 0) {
|
|
5611
|
+
executionNotes.push("Good type hierarchy");
|
|
5612
|
+
}
|
|
5613
|
+
professionalExecution = Math.max(0, Math.min(10, professionalExecution));
|
|
5614
|
+
const totalScore = glanceTest + oneIdea + dataInkRatio + professionalExecution;
|
|
5615
|
+
const visualImpact = Math.round((glanceTest + oneIdea) / 2);
|
|
5616
|
+
const contentClarity = oneIdea;
|
|
5617
|
+
const professionalPolish = Math.round((dataInkRatio + professionalExecution) / 2);
|
|
5618
|
+
const themeCoherence = professionalExecution;
|
|
5408
5619
|
return {
|
|
5409
5620
|
slideIndex,
|
|
5410
5621
|
slideType,
|
|
5622
|
+
// New expert dimensions
|
|
5623
|
+
glanceTest,
|
|
5624
|
+
glanceTestNotes: glanceNotes.join("; ") || "Passes glance test",
|
|
5625
|
+
oneIdea,
|
|
5626
|
+
oneIdeaNotes: oneIdeaNotes.join("; ") || "Clear single message",
|
|
5627
|
+
dataInkRatio,
|
|
5628
|
+
dataInkNotes: dataInkNotes.join("; ") || "Good information density",
|
|
5629
|
+
professionalExecution,
|
|
5630
|
+
professionalExecutionNotes: executionNotes.join("; ") || "Professional quality",
|
|
5631
|
+
// Critical failures
|
|
5632
|
+
hasCriticalFailure: false,
|
|
5633
|
+
criticalFailures: [],
|
|
5634
|
+
// Legacy dimensions (for compatibility)
|
|
5411
5635
|
visualImpact,
|
|
5412
|
-
visualImpactNotes:
|
|
5636
|
+
visualImpactNotes: glanceNotes.join("; ") || "Standard",
|
|
5413
5637
|
contentClarity,
|
|
5414
|
-
contentClarityNotes:
|
|
5638
|
+
contentClarityNotes: oneIdeaNotes.join("; ") || "Good",
|
|
5415
5639
|
professionalPolish,
|
|
5416
|
-
professionalPolishNotes:
|
|
5640
|
+
professionalPolishNotes: executionNotes.join("; ") || "Acceptable",
|
|
5417
5641
|
themeCoherence,
|
|
5418
|
-
themeCoherenceNotes:
|
|
5642
|
+
themeCoherenceNotes: "Consistent",
|
|
5419
5643
|
totalScore,
|
|
5420
5644
|
screenshotPath
|
|
5421
5645
|
};
|
|
5422
5646
|
}
|
|
5647
|
+
/**
|
|
5648
|
+
* Create a failed slide score (for critical failures)
|
|
5649
|
+
*/
|
|
5650
|
+
createFailedSlideScore(slideIndex, slideType, reason, screenshotPath, criticalFailures = []) {
|
|
5651
|
+
return {
|
|
5652
|
+
slideIndex,
|
|
5653
|
+
slideType,
|
|
5654
|
+
glanceTest: 0,
|
|
5655
|
+
glanceTestNotes: "CRITICAL FAILURE: " + reason,
|
|
5656
|
+
oneIdea: 0,
|
|
5657
|
+
oneIdeaNotes: "CRITICAL FAILURE: " + reason,
|
|
5658
|
+
dataInkRatio: 0,
|
|
5659
|
+
dataInkNotes: "CRITICAL FAILURE: " + reason,
|
|
5660
|
+
professionalExecution: 0,
|
|
5661
|
+
professionalExecutionNotes: "CRITICAL FAILURE: " + reason,
|
|
5662
|
+
hasCriticalFailure: true,
|
|
5663
|
+
criticalFailures: criticalFailures.length > 0 ? criticalFailures : [reason],
|
|
5664
|
+
visualImpact: 0,
|
|
5665
|
+
visualImpactNotes: "CRITICAL: " + reason,
|
|
5666
|
+
contentClarity: 0,
|
|
5667
|
+
contentClarityNotes: "CRITICAL: " + reason,
|
|
5668
|
+
professionalPolish: 0,
|
|
5669
|
+
professionalPolishNotes: "CRITICAL: " + reason,
|
|
5670
|
+
themeCoherence: 0,
|
|
5671
|
+
themeCoherenceNotes: "CRITICAL: " + reason,
|
|
5672
|
+
totalScore: 0,
|
|
5673
|
+
screenshotPath
|
|
5674
|
+
};
|
|
5675
|
+
}
|
|
5423
5676
|
inferSlideType(slideData) {
|
|
5424
5677
|
const classList = slideData.classList || [];
|
|
5425
5678
|
for (const cls of classList) {
|
|
@@ -5531,34 +5784,44 @@ var VisualQualityEvaluator = class {
|
|
|
5531
5784
|
evaluateContentQuality(slideScores) {
|
|
5532
5785
|
let score = 25;
|
|
5533
5786
|
const notes = [];
|
|
5534
|
-
const
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5787
|
+
const criticalFailureSlides = slideScores.filter((s) => s.hasCriticalFailure);
|
|
5788
|
+
if (criticalFailureSlides.length > 0) {
|
|
5789
|
+
score = Math.max(0, 25 - criticalFailureSlides.length * 8);
|
|
5790
|
+
notes.push(`CRITICAL: ${criticalFailureSlides.length} slide(s) have critical failures`);
|
|
5791
|
+
criticalFailureSlides.forEach((s) => {
|
|
5792
|
+
s.criticalFailures.forEach((f) => notes.push(` - Slide ${s.slideIndex}: ${f}`));
|
|
5793
|
+
});
|
|
5540
5794
|
}
|
|
5541
|
-
const
|
|
5542
|
-
const
|
|
5795
|
+
const avgGlance = slideScores.reduce((sum, s) => sum + s.glanceTest, 0) / slideScores.length;
|
|
5796
|
+
const passesGlanceTest = avgGlance >= 6;
|
|
5797
|
+
if (!passesGlanceTest) {
|
|
5798
|
+
score -= 5;
|
|
5799
|
+
notes.push(`Glance Test: Average ${avgGlance.toFixed(1)}/10 - too text-heavy`);
|
|
5800
|
+
}
|
|
5801
|
+
const avgOneIdea = slideScores.reduce((sum, s) => sum + s.oneIdea, 0) / slideScores.length;
|
|
5802
|
+
const hasOneIdeaPerSlide = avgOneIdea >= 6;
|
|
5803
|
+
if (!hasOneIdeaPerSlide) {
|
|
5804
|
+
score -= 5;
|
|
5805
|
+
notes.push(`One Idea Rule: Average ${avgOneIdea.toFixed(1)}/10 - messages diluted`);
|
|
5806
|
+
}
|
|
5807
|
+
const messagesAreClear = avgOneIdea >= 7;
|
|
5808
|
+
const lowScoreSlides = slideScores.filter((s) => s.totalScore < 20).length;
|
|
5809
|
+
const appropriateDepth = lowScoreSlides < slideScores.length * 0.2;
|
|
5543
5810
|
if (!appropriateDepth) {
|
|
5544
5811
|
score -= 5;
|
|
5545
|
-
notes.push("Some slides have
|
|
5812
|
+
notes.push("Some slides have quality issues");
|
|
5546
5813
|
}
|
|
5547
|
-
const overloadedSlides = slideScores.filter(
|
|
5548
|
-
(s) => s.contentClarityNotes.includes("too long") || s.visualImpactNotes.includes("Too much")
|
|
5549
|
-
).length;
|
|
5814
|
+
const overloadedSlides = slideScores.filter((s) => s.glanceTest < 5).length;
|
|
5550
5815
|
const noOverload = overloadedSlides === 0;
|
|
5551
5816
|
if (!noOverload) {
|
|
5552
|
-
score -=
|
|
5553
|
-
notes.push(`${overloadedSlides} slides
|
|
5817
|
+
score -= 3;
|
|
5818
|
+
notes.push(`${overloadedSlides} slides fail glance test - too dense`);
|
|
5554
5819
|
}
|
|
5555
|
-
const
|
|
5556
|
-
|
|
5557
|
-
).length;
|
|
5558
|
-
const actionableInsights = insightSlides >= slideScores.length * 0.3;
|
|
5820
|
+
const excellentSlides = slideScores.filter((s) => s.totalScore >= 30).length;
|
|
5821
|
+
const actionableInsights = excellentSlides >= slideScores.length * 0.3;
|
|
5559
5822
|
if (!actionableInsights) {
|
|
5560
|
-
score -=
|
|
5561
|
-
notes.push("Need more high-impact
|
|
5823
|
+
score -= 3;
|
|
5824
|
+
notes.push("Need more high-impact slides");
|
|
5562
5825
|
}
|
|
5563
5826
|
return {
|
|
5564
5827
|
score: Math.max(0, score),
|
|
@@ -5572,30 +5835,50 @@ var VisualQualityEvaluator = class {
|
|
|
5572
5835
|
evaluateExecutiveReadiness(slideScores) {
|
|
5573
5836
|
let score = 25;
|
|
5574
5837
|
const notes = [];
|
|
5575
|
-
const
|
|
5576
|
-
|
|
5577
|
-
|
|
5838
|
+
const criticalFailureSlides = slideScores.filter((s) => s.hasCriticalFailure);
|
|
5839
|
+
if (criticalFailureSlides.length > 0) {
|
|
5840
|
+
score = 0;
|
|
5841
|
+
notes.push(`CRITICAL: ${criticalFailureSlides.length} slide(s) have critical failures - CANNOT show to executives`);
|
|
5842
|
+
criticalFailureSlides.forEach((s) => {
|
|
5843
|
+
notes.push(` - Slide ${s.slideIndex} (${s.slideType}): ${s.criticalFailures[0]}`);
|
|
5844
|
+
});
|
|
5845
|
+
return {
|
|
5846
|
+
score: 0,
|
|
5847
|
+
wouldImpress: false,
|
|
5848
|
+
readyForBoardroom: false,
|
|
5849
|
+
compelling: false,
|
|
5850
|
+
shareworthy: false,
|
|
5851
|
+
notes: notes.join(". ")
|
|
5852
|
+
};
|
|
5853
|
+
}
|
|
5854
|
+
const avgGlance = slideScores.reduce((sum, s) => sum + s.glanceTest, 0) / slideScores.length;
|
|
5855
|
+
const avgOneIdea = slideScores.reduce((sum, s) => sum + s.oneIdea, 0) / slideScores.length;
|
|
5856
|
+
const avgDataInk = slideScores.reduce((sum, s) => sum + s.dataInkRatio, 0) / slideScores.length;
|
|
5857
|
+
const avgExecution = slideScores.reduce((sum, s) => sum + s.professionalExecution, 0) / slideScores.length;
|
|
5578
5858
|
const avgTotal = slideScores.reduce((sum, s) => sum + s.totalScore, 0) / slideScores.length;
|
|
5579
|
-
const wouldImpress =
|
|
5859
|
+
const wouldImpress = avgGlance >= 7 && avgOneIdea >= 7 && avgExecution >= 7;
|
|
5580
5860
|
if (!wouldImpress) {
|
|
5581
5861
|
score -= 7;
|
|
5582
5862
|
notes.push("Needs more visual impact to impress");
|
|
5583
5863
|
}
|
|
5584
|
-
const readyForBoardroom =
|
|
5864
|
+
const readyForBoardroom = avgExecution >= 6 && avgDataInk >= 6;
|
|
5585
5865
|
if (!readyForBoardroom) {
|
|
5586
5866
|
score -= 7;
|
|
5587
|
-
notes.push(
|
|
5867
|
+
notes.push(`Boardroom readiness: Execution ${avgExecution.toFixed(1)}/10, Data-Ink ${avgDataInk.toFixed(1)}/10`);
|
|
5588
5868
|
}
|
|
5589
|
-
const compelling =
|
|
5869
|
+
const compelling = avgGlance >= 6 && avgOneIdea >= 6;
|
|
5590
5870
|
if (!compelling) {
|
|
5591
5871
|
score -= 5;
|
|
5592
|
-
notes.push(
|
|
5872
|
+
notes.push(`Compelling: Glance ${avgGlance.toFixed(1)}/10, OneIdea ${avgOneIdea.toFixed(1)}/10`);
|
|
5593
5873
|
}
|
|
5594
5874
|
const excellentSlides = slideScores.filter((s) => s.totalScore >= 30).length;
|
|
5595
5875
|
const shareworthy = excellentSlides >= slideScores.length * 0.4;
|
|
5596
5876
|
if (!shareworthy) {
|
|
5597
5877
|
score -= 5;
|
|
5598
|
-
notes.push(
|
|
5878
|
+
notes.push(`Shareworthy: ${excellentSlides}/${slideScores.length} excellent slides (need 40%)`);
|
|
5879
|
+
}
|
|
5880
|
+
if (wouldImpress && readyForBoardroom && compelling) {
|
|
5881
|
+
notes.push("Meets expert standards - McKinsey/TED quality");
|
|
5599
5882
|
}
|
|
5600
5883
|
return {
|
|
5601
5884
|
score: Math.max(0, score),
|