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.js
CHANGED
|
@@ -847,7 +847,8 @@ var KnowledgeGateway = class {
|
|
|
847
847
|
// Subtitle needs more room for complete phrases - at least 10 words for keynote
|
|
848
848
|
subtitle: { maxWords: mode === "keynote" ? 12 : Math.min(20, Math.floor(maxWords / 2)) },
|
|
849
849
|
context: { maxWords: Math.min(30, Math.floor(maxWords / 2)) },
|
|
850
|
-
|
|
850
|
+
// Step descriptions need minimum 8 words for meaningful content
|
|
851
|
+
step: { maxWords: Math.max(8, Math.min(20, Math.floor(maxWords / 3))) },
|
|
851
852
|
columnContent: { maxWords: Math.min(25, Math.floor(maxWords / 3)) }
|
|
852
853
|
};
|
|
853
854
|
}
|
|
@@ -2895,9 +2896,8 @@ var SlideFactory = class {
|
|
|
2895
2896
|
const steps = this.classifier.extractSteps(section);
|
|
2896
2897
|
const maxSteps = Math.min(
|
|
2897
2898
|
steps.length,
|
|
2898
|
-
this.config.rules.bulletsPerSlide.max,
|
|
2899
2899
|
this.config.millersLaw.maxItems
|
|
2900
|
-
// FROM KB - 7±2 rule
|
|
2900
|
+
// FROM KB - 7±2 rule (max 9)
|
|
2901
2901
|
);
|
|
2902
2902
|
return {
|
|
2903
2903
|
index,
|
|
@@ -2919,7 +2919,7 @@ var SlideFactory = class {
|
|
|
2919
2919
|
}
|
|
2920
2920
|
createProcessSlide(index, section) {
|
|
2921
2921
|
const steps = this.classifier.extractSteps(section);
|
|
2922
|
-
const maxSteps = Math.min(steps.length, this.config.
|
|
2922
|
+
const maxSteps = Math.min(steps.length, this.config.millersLaw.maxItems);
|
|
2923
2923
|
return {
|
|
2924
2924
|
index,
|
|
2925
2925
|
type: "process",
|
|
@@ -5326,169 +5326,422 @@ var VisualQualityEvaluator = class {
|
|
|
5326
5326
|
const title = currentSlide.querySelector("h1, h2, .title")?.textContent?.trim() || "";
|
|
5327
5327
|
const body = currentSlide.querySelector(".body, p:not(.subtitle)")?.textContent?.trim() || "";
|
|
5328
5328
|
const bullets = Array.from(currentSlide.querySelectorAll("li")).map((li) => li.textContent?.trim() || "");
|
|
5329
|
+
const hasSteps = !!currentSlide.querySelector(".steps, .process-steps, .timeline");
|
|
5330
|
+
const steps = Array.from(currentSlide.querySelectorAll(".step, .process-step, .timeline-item")).map(
|
|
5331
|
+
(s) => s.textContent?.trim() || ""
|
|
5332
|
+
);
|
|
5333
|
+
const hasMetrics = !!currentSlide.querySelector(".metrics, .metric");
|
|
5329
5334
|
const hasImage = !!currentSlide.querySelector("img");
|
|
5330
5335
|
const hasChart = !!currentSlide.querySelector(".chart, svg, canvas");
|
|
5331
5336
|
const classList = Array.from(currentSlide.classList);
|
|
5332
5337
|
const backgroundColor = window.getComputedStyle(currentSlide).backgroundColor;
|
|
5333
5338
|
const titleEl = currentSlide.querySelector("h1, h2, .title");
|
|
5334
5339
|
const titleStyles = titleEl ? window.getComputedStyle(titleEl) : null;
|
|
5340
|
+
const truncatedElements = [];
|
|
5341
|
+
const contentElements = currentSlide.querySelectorAll("h1, h2, h3, p, span, li, .body, .step-desc, .step-title, .timeline-content");
|
|
5342
|
+
contentElements.forEach((el, idx) => {
|
|
5343
|
+
const styles = window.getComputedStyle(el);
|
|
5344
|
+
const text = el.textContent?.trim() || "";
|
|
5345
|
+
if (text.length < 15) return;
|
|
5346
|
+
const isLayoutContainer = el.classList?.contains("slide-content") || el.classList?.contains("steps") || el.classList?.contains("timeline") || el.classList?.contains("process-steps");
|
|
5347
|
+
if (isLayoutContainer) return;
|
|
5348
|
+
if (styles.textOverflow === "ellipsis") {
|
|
5349
|
+
if (el.scrollWidth > el.clientWidth + 5) {
|
|
5350
|
+
truncatedElements.push(`Element ${idx}: "${text.substring(0, 30)}..." is truncated horizontally`);
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
const scrollHeight = el.scrollHeight;
|
|
5354
|
+
const clientHeight = el.clientHeight;
|
|
5355
|
+
if (scrollHeight > clientHeight + 20) {
|
|
5356
|
+
const overflow = styles.overflow || styles.overflowY;
|
|
5357
|
+
if (overflow === "hidden" || overflow === "clip") {
|
|
5358
|
+
truncatedElements.push(`Element ${idx}: "${text.substring(0, 30)}..." is truncated vertically`);
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
});
|
|
5362
|
+
const allVisibleText = Array.from(currentSlide.querySelectorAll("*")).map((el) => el.textContent?.trim() || "").join(" ").trim();
|
|
5363
|
+
const isEmptySlide = allVisibleText.length < 10 && !hasImage && !hasChart;
|
|
5364
|
+
const hasOnlyTitle = title.length > 0 && body.length === 0 && bullets.length === 0 && steps.length === 0 && !hasSteps && !hasMetrics && !hasImage && !hasChart;
|
|
5365
|
+
const titleLower = title.toLowerCase();
|
|
5366
|
+
const bodyLower = body.toLowerCase();
|
|
5367
|
+
const isRedundant = titleLower.length > 10 && bodyLower.length > 10 && (titleLower.includes(bodyLower) || bodyLower.includes(titleLower));
|
|
5368
|
+
const contrastIssues = [];
|
|
5369
|
+
const parseRGB = (color) => {
|
|
5370
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
5371
|
+
if (match && match[1] !== void 0 && match[2] !== void 0 && match[3] !== void 0) {
|
|
5372
|
+
return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) };
|
|
5373
|
+
}
|
|
5374
|
+
return null;
|
|
5375
|
+
};
|
|
5376
|
+
const getLuminance = (rgb) => {
|
|
5377
|
+
const values = [rgb.r, rgb.g, rgb.b].map((c) => {
|
|
5378
|
+
c = c / 255;
|
|
5379
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
5380
|
+
});
|
|
5381
|
+
return 0.2126 * (values[0] ?? 0) + 0.7152 * (values[1] ?? 0) + 0.0722 * (values[2] ?? 0);
|
|
5382
|
+
};
|
|
5383
|
+
const getContrastRatio = (fg, bg) => {
|
|
5384
|
+
const l1 = getLuminance(fg);
|
|
5385
|
+
const l2 = getLuminance(bg);
|
|
5386
|
+
const lighter = Math.max(l1, l2);
|
|
5387
|
+
const darker = Math.min(l1, l2);
|
|
5388
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
5389
|
+
};
|
|
5390
|
+
const bgRGB = parseRGB(backgroundColor);
|
|
5391
|
+
if (titleEl && bgRGB) {
|
|
5392
|
+
const titleRGB = parseRGB(titleStyles?.color || "");
|
|
5393
|
+
if (titleRGB) {
|
|
5394
|
+
const contrast = getContrastRatio(titleRGB, bgRGB);
|
|
5395
|
+
if (contrast < 4.5) {
|
|
5396
|
+
contrastIssues.push(`Title contrast ${contrast.toFixed(1)}:1 (need 4.5:1)`);
|
|
5397
|
+
}
|
|
5398
|
+
}
|
|
5399
|
+
}
|
|
5400
|
+
const bodyEl = currentSlide.querySelector(".body, p:not(.subtitle)");
|
|
5401
|
+
if (bodyEl && bgRGB) {
|
|
5402
|
+
const bodyStyles2 = window.getComputedStyle(bodyEl);
|
|
5403
|
+
const bodyRGB = parseRGB(bodyStyles2.color);
|
|
5404
|
+
if (bodyRGB) {
|
|
5405
|
+
const contrast = getContrastRatio(bodyRGB, bgRGB);
|
|
5406
|
+
if (contrast < 4.5) {
|
|
5407
|
+
contrastIssues.push(`Body contrast ${contrast.toFixed(1)}:1 (need 4.5:1)`);
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
const hasContrastIssues = contrastIssues.length > 0;
|
|
5412
|
+
const layoutIssues = [];
|
|
5413
|
+
const slideRect = currentSlide.getBoundingClientRect();
|
|
5414
|
+
const layoutElements = currentSlide.querySelectorAll("h1, h2, h3, p, ul, ol, .number, .metric, img, canvas, svg");
|
|
5415
|
+
let topHeavy = 0;
|
|
5416
|
+
let bottomHeavy = 0;
|
|
5417
|
+
let leftHeavy = 0;
|
|
5418
|
+
let rightHeavy = 0;
|
|
5419
|
+
let centerY = slideRect.height / 2;
|
|
5420
|
+
let centerX = slideRect.width / 2;
|
|
5421
|
+
layoutElements.forEach((el) => {
|
|
5422
|
+
const rect = el.getBoundingClientRect();
|
|
5423
|
+
const elCenterY = rect.top + rect.height / 2 - slideRect.top;
|
|
5424
|
+
const elCenterX = rect.left + rect.width / 2 - slideRect.left;
|
|
5425
|
+
if (elCenterY < centerY) topHeavy++;
|
|
5426
|
+
else bottomHeavy++;
|
|
5427
|
+
if (elCenterX < centerX) leftHeavy++;
|
|
5428
|
+
else rightHeavy++;
|
|
5429
|
+
});
|
|
5430
|
+
const verticalBalance = Math.abs(topHeavy - bottomHeavy) / Math.max(1, topHeavy + bottomHeavy);
|
|
5431
|
+
const horizontalBalance = Math.abs(leftHeavy - rightHeavy) / Math.max(1, leftHeavy + rightHeavy);
|
|
5432
|
+
if (verticalBalance > 0.6 && contentElements.length > 2) {
|
|
5433
|
+
layoutIssues.push(`Vertical imbalance: ${topHeavy} top vs ${bottomHeavy} bottom`);
|
|
5434
|
+
}
|
|
5435
|
+
if (horizontalBalance > 0.6 && contentElements.length > 2) {
|
|
5436
|
+
layoutIssues.push(`Horizontal imbalance: ${leftHeavy} left vs ${rightHeavy} right`);
|
|
5437
|
+
}
|
|
5438
|
+
const hasLayoutIssues = layoutIssues.length > 0;
|
|
5439
|
+
const typographyIssues = [];
|
|
5440
|
+
const titleSize = parseFloat(titleStyles?.fontSize || "0");
|
|
5441
|
+
const bodyStyles = bodyEl ? window.getComputedStyle(bodyEl) : null;
|
|
5442
|
+
const bodySize = parseFloat(bodyStyles?.fontSize || "0");
|
|
5443
|
+
if (titleSize > 0 && bodySize > 0 && titleSize <= bodySize) {
|
|
5444
|
+
typographyIssues.push("Title not larger than body - hierarchy broken");
|
|
5445
|
+
}
|
|
5446
|
+
if (titleSize > 0 && bodySize > 0 && titleSize < bodySize * 1.3) {
|
|
5447
|
+
typographyIssues.push("Title not prominent enough vs body");
|
|
5448
|
+
}
|
|
5449
|
+
const hasTypographyIssues = typographyIssues.length > 0;
|
|
5450
|
+
const completenessIssues = [];
|
|
5451
|
+
const emptySteps = currentSlide.querySelectorAll(".steps:empty, .timeline:empty, .process-steps:empty, .steps > :empty");
|
|
5452
|
+
if (emptySteps.length > 0) {
|
|
5453
|
+
completenessIssues.push(`${emptySteps.length} empty step/timeline container(s) - content missing`);
|
|
5454
|
+
}
|
|
5455
|
+
if (body.includes("Lorem") || body.includes("placeholder") || body.includes("TODO")) {
|
|
5456
|
+
completenessIssues.push("Contains placeholder text");
|
|
5457
|
+
}
|
|
5458
|
+
const emptyBullets = bullets.filter((b) => b.length < 3).length;
|
|
5459
|
+
if (emptyBullets > 0 && bullets.length > 0) {
|
|
5460
|
+
completenessIssues.push(`${emptyBullets} empty/minimal bullets`);
|
|
5461
|
+
}
|
|
5462
|
+
const hasCompletenessIssues = completenessIssues.length > 0;
|
|
5463
|
+
const visualNeedsIssues = [];
|
|
5464
|
+
const visualRequiredTypes = ["data", "chart", "metrics", "comparison", "process", "timeline"];
|
|
5465
|
+
const visualRecommendedTypes = ["big-number", "quote", "testimonial"];
|
|
5466
|
+
const slideClasses = Array.from(currentSlide.classList).join(" ").toLowerCase();
|
|
5467
|
+
const needsVisual = visualRequiredTypes.some((t) => slideClasses.includes(t));
|
|
5468
|
+
const visualRecommended = visualRecommendedTypes.some((t) => slideClasses.includes(t));
|
|
5469
|
+
const contentText = (title + " " + body).toLowerCase();
|
|
5470
|
+
const dataIndicators = /\d+%|\$[\d,]+|\d+x|million|billion|percent|growth|increase|decrease|comparison|versus|vs\b|before|after/i;
|
|
5471
|
+
const hasDataContent = dataIndicators.test(contentText);
|
|
5472
|
+
const hasAnyVisual = hasImage || hasChart;
|
|
5473
|
+
if (needsVisual && !hasAnyVisual) {
|
|
5474
|
+
visualNeedsIssues.push("Data/process slide missing visual - needs chart, diagram, or image");
|
|
5475
|
+
} else if (hasDataContent && !hasAnyVisual && bullets.length === 0) {
|
|
5476
|
+
visualNeedsIssues.push("Content has numbers/data but no visualization - chart would help");
|
|
5477
|
+
} else if (visualRecommended && !hasAnyVisual) {
|
|
5478
|
+
visualNeedsIssues.push("Visual recommended for this slide type");
|
|
5479
|
+
}
|
|
5480
|
+
if (hasImage) {
|
|
5481
|
+
const img = currentSlide.querySelector("img");
|
|
5482
|
+
const imgSrc = img?.getAttribute("src") || "";
|
|
5483
|
+
if (imgSrc.includes("placeholder") || imgSrc.includes("picsum") || imgSrc.includes("via.placeholder")) {
|
|
5484
|
+
visualNeedsIssues.push("Placeholder image detected - needs real visual");
|
|
5485
|
+
}
|
|
5486
|
+
}
|
|
5487
|
+
const needsVisualButMissing = visualNeedsIssues.length > 0;
|
|
5488
|
+
const visualScore = hasAnyVisual ? 10 : needsVisual || hasDataContent ? 0 : 5;
|
|
5335
5489
|
return {
|
|
5336
5490
|
title,
|
|
5337
5491
|
body,
|
|
5338
5492
|
bullets,
|
|
5493
|
+
steps,
|
|
5494
|
+
// NEW: Timeline/process step content
|
|
5495
|
+
hasSteps,
|
|
5496
|
+
// NEW: Has .steps/.process-steps container
|
|
5497
|
+
hasMetrics,
|
|
5498
|
+
// NEW: Has metrics content
|
|
5339
5499
|
hasImage,
|
|
5340
5500
|
hasChart,
|
|
5341
5501
|
classList,
|
|
5342
5502
|
backgroundColor,
|
|
5343
5503
|
titleFontSize: titleStyles?.fontSize || "",
|
|
5344
5504
|
titleColor: titleStyles?.color || "",
|
|
5345
|
-
|
|
5505
|
+
// Include steps in content length calculation
|
|
5506
|
+
contentLength: (title + body + bullets.join(" ") + steps.join(" ")).length,
|
|
5507
|
+
// Visual quality flags
|
|
5508
|
+
hasTruncatedText: truncatedElements.length > 0,
|
|
5509
|
+
truncatedElements,
|
|
5510
|
+
isEmptySlide,
|
|
5511
|
+
hasOnlyTitle,
|
|
5512
|
+
isRedundant,
|
|
5513
|
+
allVisibleTextLength: allVisibleText.length,
|
|
5514
|
+
// NEW: Expert quality checks
|
|
5515
|
+
hasContrastIssues,
|
|
5516
|
+
contrastIssues,
|
|
5517
|
+
hasLayoutIssues,
|
|
5518
|
+
layoutIssues,
|
|
5519
|
+
hasTypographyIssues,
|
|
5520
|
+
typographyIssues,
|
|
5521
|
+
hasCompletenessIssues,
|
|
5522
|
+
completenessIssues,
|
|
5523
|
+
// Visual needs analysis
|
|
5524
|
+
needsVisualButMissing,
|
|
5525
|
+
visualNeedsIssues,
|
|
5526
|
+
visualScore
|
|
5346
5527
|
};
|
|
5347
5528
|
});
|
|
5348
5529
|
return this.scoreSlide(slideIndex, slideData, screenshotPath);
|
|
5349
5530
|
}
|
|
5531
|
+
/**
|
|
5532
|
+
* EXPERT-LEVEL SLIDE SCORING
|
|
5533
|
+
*
|
|
5534
|
+
* This evaluates each slide like Nancy Duarte, Carmine Gallo, or a McKinsey partner would.
|
|
5535
|
+
* It's not about rules - it's about whether the slide WORKS.
|
|
5536
|
+
*/
|
|
5350
5537
|
scoreSlide(slideIndex, slideData, screenshotPath) {
|
|
5351
5538
|
if (!slideData) {
|
|
5352
|
-
return
|
|
5353
|
-
slideIndex,
|
|
5354
|
-
slideType: "unknown",
|
|
5355
|
-
visualImpact: 0,
|
|
5356
|
-
visualImpactNotes: "Could not analyze slide",
|
|
5357
|
-
contentClarity: 0,
|
|
5358
|
-
contentClarityNotes: "Could not analyze slide",
|
|
5359
|
-
professionalPolish: 0,
|
|
5360
|
-
professionalPolishNotes: "Could not analyze slide",
|
|
5361
|
-
themeCoherence: 0,
|
|
5362
|
-
themeCoherenceNotes: "Could not analyze slide",
|
|
5363
|
-
totalScore: 0,
|
|
5364
|
-
screenshotPath
|
|
5365
|
-
};
|
|
5539
|
+
return this.createFailedSlideScore(slideIndex, "unknown", "Could not analyze slide", screenshotPath);
|
|
5366
5540
|
}
|
|
5367
5541
|
const slideType = this.inferSlideType(slideData);
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
visualImpact += 2;
|
|
5372
|
-
visualNotes.push("Has imagery");
|
|
5542
|
+
const criticalFailures = [];
|
|
5543
|
+
if (slideData.isEmptySlide) {
|
|
5544
|
+
criticalFailures.push("EMPTY SLIDE: No meaningful content - slide serves no purpose");
|
|
5373
5545
|
}
|
|
5374
|
-
if (slideData.
|
|
5375
|
-
|
|
5376
|
-
visualNotes.push("Has data visualization");
|
|
5546
|
+
if (slideData.hasOnlyTitle && !["title", "agenda", "thank-you", "cta"].includes(slideType)) {
|
|
5547
|
+
criticalFailures.push("INCOMPLETE: Slide has title but no body content - looks unfinished");
|
|
5377
5548
|
}
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
"big_number",
|
|
5381
|
-
"metrics-grid",
|
|
5382
|
-
"metrics_grid",
|
|
5383
|
-
"three-column",
|
|
5384
|
-
"three_column",
|
|
5385
|
-
"three-points",
|
|
5386
|
-
"three_points",
|
|
5387
|
-
"title-impact",
|
|
5388
|
-
"title_impact",
|
|
5389
|
-
"cta",
|
|
5390
|
-
"call-to-action",
|
|
5391
|
-
"comparison",
|
|
5392
|
-
"timeline",
|
|
5393
|
-
"process",
|
|
5394
|
-
"quote",
|
|
5395
|
-
"testimonial"
|
|
5396
|
-
];
|
|
5397
|
-
if (highImpactTypes.some((t) => slideType.includes(t.replace(/_/g, "-")) || slideType.includes(t.replace(/-/g, "_")))) {
|
|
5398
|
-
visualImpact += 2;
|
|
5399
|
-
visualNotes.push("High-impact slide type");
|
|
5549
|
+
if (slideData.hasTruncatedText) {
|
|
5550
|
+
criticalFailures.push(`TRUNCATED TEXT: ${slideData.truncatedElements.length} element(s) cut off - message incomplete`);
|
|
5400
5551
|
}
|
|
5401
|
-
if (
|
|
5402
|
-
|
|
5403
|
-
visualNotes.push("Clean single statement");
|
|
5552
|
+
if (slideData.isRedundant) {
|
|
5553
|
+
criticalFailures.push("REDUNDANT: Title and body say the same thing - violates one-idea rule");
|
|
5404
5554
|
}
|
|
5405
|
-
if (
|
|
5406
|
-
|
|
5407
|
-
|
|
5555
|
+
if (slideData.hasCompletenessIssues) {
|
|
5556
|
+
slideData.completenessIssues.forEach((issue) => {
|
|
5557
|
+
if (issue.includes("empty")) {
|
|
5558
|
+
criticalFailures.push(`INCOMPLETE: ${issue}`);
|
|
5559
|
+
}
|
|
5560
|
+
});
|
|
5408
5561
|
}
|
|
5409
|
-
if (
|
|
5410
|
-
|
|
5411
|
-
|
|
5562
|
+
if (criticalFailures.length > 0) {
|
|
5563
|
+
return this.createFailedSlideScore(slideIndex, slideType, criticalFailures.join("; "), screenshotPath, criticalFailures);
|
|
5564
|
+
}
|
|
5565
|
+
let glanceTest = 8;
|
|
5566
|
+
const glanceNotes = [];
|
|
5567
|
+
const wordCount = slideData.contentLength / 6;
|
|
5568
|
+
if (wordCount > 40) {
|
|
5569
|
+
glanceTest = 3;
|
|
5570
|
+
glanceNotes.push("Too much text - fails glance test");
|
|
5571
|
+
} else if (wordCount > 25) {
|
|
5572
|
+
glanceTest -= 3;
|
|
5573
|
+
glanceNotes.push("Text-heavy - borderline glance test");
|
|
5574
|
+
} else if (wordCount < 15) {
|
|
5575
|
+
glanceTest += 1;
|
|
5576
|
+
glanceNotes.push("Clean, minimal text - passes glance test easily");
|
|
5412
5577
|
}
|
|
5413
5578
|
if (slideData.bullets.length > 5) {
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
}
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
if (slideData.
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
}
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
if (
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
}
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5579
|
+
glanceTest -= 4;
|
|
5580
|
+
glanceNotes.push("Too many bullets - cannot scan in 3 seconds");
|
|
5581
|
+
} else if (slideData.bullets.length > 3) {
|
|
5582
|
+
glanceTest -= 2;
|
|
5583
|
+
glanceNotes.push("Multiple bullets - needs careful structuring");
|
|
5584
|
+
}
|
|
5585
|
+
if (slideData.hasImage || slideData.hasChart) {
|
|
5586
|
+
glanceTest += 1;
|
|
5587
|
+
glanceNotes.push("Visual element aids quick comprehension");
|
|
5588
|
+
}
|
|
5589
|
+
glanceTest = Math.max(0, Math.min(10, glanceTest));
|
|
5590
|
+
let oneIdea = 7;
|
|
5591
|
+
const oneIdeaNotes = [];
|
|
5592
|
+
if (slideData.title.length > 0 && slideData.title.length < 60) {
|
|
5593
|
+
oneIdea += 1;
|
|
5594
|
+
oneIdeaNotes.push("Clear, focused title");
|
|
5595
|
+
} else if (slideData.title.length > 80) {
|
|
5596
|
+
oneIdea -= 2;
|
|
5597
|
+
oneIdeaNotes.push("Title too long - multiple ideas?");
|
|
5598
|
+
} else if (slideData.title.length === 0) {
|
|
5599
|
+
oneIdea -= 4;
|
|
5600
|
+
oneIdeaNotes.push("No title - what is the one idea?");
|
|
5601
|
+
}
|
|
5602
|
+
const focusedTypes = ["big-number", "single-statement", "quote", "cta", "title"];
|
|
5603
|
+
if (focusedTypes.some((t) => slideType.includes(t))) {
|
|
5604
|
+
oneIdea += 2;
|
|
5605
|
+
oneIdeaNotes.push("Slide type naturally focuses on one idea");
|
|
5606
|
+
}
|
|
5607
|
+
if (slideData.bullets.length > 4) {
|
|
5608
|
+
oneIdea -= 3;
|
|
5609
|
+
oneIdeaNotes.push("Multiple bullets dilute the message");
|
|
5610
|
+
}
|
|
5611
|
+
oneIdea = Math.max(0, Math.min(10, oneIdea));
|
|
5612
|
+
let dataInkRatio = 7;
|
|
5613
|
+
const dataInkNotes = [];
|
|
5614
|
+
const structuredTypes = ["big-number", "metrics-grid", "three-column", "timeline", "process", "comparison"];
|
|
5615
|
+
if (structuredTypes.some((t) => slideType.includes(t))) {
|
|
5616
|
+
dataInkRatio += 1;
|
|
5617
|
+
dataInkNotes.push("Structured layout");
|
|
5618
|
+
}
|
|
5619
|
+
if (slideData.hasChart) {
|
|
5620
|
+
dataInkRatio += 2;
|
|
5621
|
+
dataInkNotes.push("Data visualization present - excellent");
|
|
5622
|
+
}
|
|
5623
|
+
if (slideData.hasImage) {
|
|
5624
|
+
dataInkRatio += 1;
|
|
5625
|
+
dataInkNotes.push("Supporting visual present");
|
|
5626
|
+
}
|
|
5627
|
+
if (slideData.needsVisualButMissing) {
|
|
5628
|
+
dataInkRatio -= 3;
|
|
5629
|
+
slideData.visualNeedsIssues.forEach((issue) => {
|
|
5630
|
+
dataInkNotes.push(`Visual: ${issue}`);
|
|
5631
|
+
});
|
|
5632
|
+
} else if (slideData.visualScore === 10) {
|
|
5633
|
+
dataInkNotes.push("Appropriate visuals for content type");
|
|
5634
|
+
}
|
|
5635
|
+
if (slideData.contentLength > 400) {
|
|
5636
|
+
dataInkRatio -= 4;
|
|
5637
|
+
dataInkNotes.push("Excessive text - low information density");
|
|
5638
|
+
}
|
|
5639
|
+
dataInkRatio = Math.max(0, Math.min(10, dataInkRatio));
|
|
5640
|
+
let professionalExecution = 7;
|
|
5641
|
+
const executionNotes = [];
|
|
5440
5642
|
const titleFontSize = parseFloat(slideData.titleFontSize || "0");
|
|
5441
5643
|
if (titleFontSize >= 40) {
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
} else if (titleFontSize < 24) {
|
|
5445
|
-
|
|
5446
|
-
|
|
5644
|
+
professionalExecution += 1;
|
|
5645
|
+
executionNotes.push("Strong title typography");
|
|
5646
|
+
} else if (titleFontSize > 0 && titleFontSize < 24) {
|
|
5647
|
+
professionalExecution -= 1;
|
|
5648
|
+
executionNotes.push("Title could be more prominent");
|
|
5447
5649
|
}
|
|
5448
5650
|
if (slideData.contentLength > 10 && slideData.contentLength < 200) {
|
|
5449
|
-
|
|
5450
|
-
|
|
5651
|
+
professionalExecution += 1;
|
|
5652
|
+
executionNotes.push("Well-balanced content density");
|
|
5451
5653
|
}
|
|
5452
|
-
const polishedTypes = [
|
|
5453
|
-
"big-number",
|
|
5454
|
-
"metrics-grid",
|
|
5455
|
-
"three-column",
|
|
5456
|
-
"three-points",
|
|
5457
|
-
"comparison",
|
|
5458
|
-
"timeline",
|
|
5459
|
-
"process",
|
|
5460
|
-
"cta",
|
|
5461
|
-
"title",
|
|
5462
|
-
"thank-you"
|
|
5463
|
-
];
|
|
5464
|
-
if (polishedTypes.some((t) => slideType.includes(t))) {
|
|
5465
|
-
professionalPolish += 1;
|
|
5466
|
-
polishNotes.push("Well-structured layout");
|
|
5467
|
-
}
|
|
5468
|
-
professionalPolish = Math.max(0, Math.min(10, professionalPolish));
|
|
5469
|
-
let themeCoherence = 7;
|
|
5470
|
-
const coherenceNotes = [];
|
|
5471
5654
|
if (slideData.classList.some((c) => c.includes("slide-"))) {
|
|
5472
|
-
|
|
5473
|
-
|
|
5655
|
+
professionalExecution += 1;
|
|
5656
|
+
executionNotes.push("Consistent with design system");
|
|
5657
|
+
}
|
|
5658
|
+
if (slideData.hasContrastIssues) {
|
|
5659
|
+
professionalExecution -= 3;
|
|
5660
|
+
slideData.contrastIssues.forEach((issue) => {
|
|
5661
|
+
executionNotes.push(`Contrast: ${issue}`);
|
|
5662
|
+
});
|
|
5663
|
+
} else {
|
|
5664
|
+
executionNotes.push("Good text contrast");
|
|
5474
5665
|
}
|
|
5475
|
-
|
|
5476
|
-
|
|
5666
|
+
if (slideData.hasLayoutIssues) {
|
|
5667
|
+
professionalExecution -= 2;
|
|
5668
|
+
slideData.layoutIssues.forEach((issue) => {
|
|
5669
|
+
executionNotes.push(`Layout: ${issue}`);
|
|
5670
|
+
});
|
|
5671
|
+
} else if (slideData.contentLength > 50) {
|
|
5672
|
+
executionNotes.push("Balanced layout");
|
|
5673
|
+
}
|
|
5674
|
+
if (slideData.hasTypographyIssues) {
|
|
5675
|
+
professionalExecution -= 2;
|
|
5676
|
+
slideData.typographyIssues.forEach((issue) => {
|
|
5677
|
+
executionNotes.push(`Typography: ${issue}`);
|
|
5678
|
+
});
|
|
5679
|
+
} else if (titleFontSize > 0) {
|
|
5680
|
+
executionNotes.push("Good type hierarchy");
|
|
5681
|
+
}
|
|
5682
|
+
professionalExecution = Math.max(0, Math.min(10, professionalExecution));
|
|
5683
|
+
const totalScore = glanceTest + oneIdea + dataInkRatio + professionalExecution;
|
|
5684
|
+
const visualImpact = Math.round((glanceTest + oneIdea) / 2);
|
|
5685
|
+
const contentClarity = oneIdea;
|
|
5686
|
+
const professionalPolish = Math.round((dataInkRatio + professionalExecution) / 2);
|
|
5687
|
+
const themeCoherence = professionalExecution;
|
|
5477
5688
|
return {
|
|
5478
5689
|
slideIndex,
|
|
5479
5690
|
slideType,
|
|
5691
|
+
// New expert dimensions
|
|
5692
|
+
glanceTest,
|
|
5693
|
+
glanceTestNotes: glanceNotes.join("; ") || "Passes glance test",
|
|
5694
|
+
oneIdea,
|
|
5695
|
+
oneIdeaNotes: oneIdeaNotes.join("; ") || "Clear single message",
|
|
5696
|
+
dataInkRatio,
|
|
5697
|
+
dataInkNotes: dataInkNotes.join("; ") || "Good information density",
|
|
5698
|
+
professionalExecution,
|
|
5699
|
+
professionalExecutionNotes: executionNotes.join("; ") || "Professional quality",
|
|
5700
|
+
// Critical failures
|
|
5701
|
+
hasCriticalFailure: false,
|
|
5702
|
+
criticalFailures: [],
|
|
5703
|
+
// Legacy dimensions (for compatibility)
|
|
5480
5704
|
visualImpact,
|
|
5481
|
-
visualImpactNotes:
|
|
5705
|
+
visualImpactNotes: glanceNotes.join("; ") || "Standard",
|
|
5482
5706
|
contentClarity,
|
|
5483
|
-
contentClarityNotes:
|
|
5707
|
+
contentClarityNotes: oneIdeaNotes.join("; ") || "Good",
|
|
5484
5708
|
professionalPolish,
|
|
5485
|
-
professionalPolishNotes:
|
|
5709
|
+
professionalPolishNotes: executionNotes.join("; ") || "Acceptable",
|
|
5486
5710
|
themeCoherence,
|
|
5487
|
-
themeCoherenceNotes:
|
|
5711
|
+
themeCoherenceNotes: "Consistent",
|
|
5488
5712
|
totalScore,
|
|
5489
5713
|
screenshotPath
|
|
5490
5714
|
};
|
|
5491
5715
|
}
|
|
5716
|
+
/**
|
|
5717
|
+
* Create a failed slide score (for critical failures)
|
|
5718
|
+
*/
|
|
5719
|
+
createFailedSlideScore(slideIndex, slideType, reason, screenshotPath, criticalFailures = []) {
|
|
5720
|
+
return {
|
|
5721
|
+
slideIndex,
|
|
5722
|
+
slideType,
|
|
5723
|
+
glanceTest: 0,
|
|
5724
|
+
glanceTestNotes: "CRITICAL FAILURE: " + reason,
|
|
5725
|
+
oneIdea: 0,
|
|
5726
|
+
oneIdeaNotes: "CRITICAL FAILURE: " + reason,
|
|
5727
|
+
dataInkRatio: 0,
|
|
5728
|
+
dataInkNotes: "CRITICAL FAILURE: " + reason,
|
|
5729
|
+
professionalExecution: 0,
|
|
5730
|
+
professionalExecutionNotes: "CRITICAL FAILURE: " + reason,
|
|
5731
|
+
hasCriticalFailure: true,
|
|
5732
|
+
criticalFailures: criticalFailures.length > 0 ? criticalFailures : [reason],
|
|
5733
|
+
visualImpact: 0,
|
|
5734
|
+
visualImpactNotes: "CRITICAL: " + reason,
|
|
5735
|
+
contentClarity: 0,
|
|
5736
|
+
contentClarityNotes: "CRITICAL: " + reason,
|
|
5737
|
+
professionalPolish: 0,
|
|
5738
|
+
professionalPolishNotes: "CRITICAL: " + reason,
|
|
5739
|
+
themeCoherence: 0,
|
|
5740
|
+
themeCoherenceNotes: "CRITICAL: " + reason,
|
|
5741
|
+
totalScore: 0,
|
|
5742
|
+
screenshotPath
|
|
5743
|
+
};
|
|
5744
|
+
}
|
|
5492
5745
|
inferSlideType(slideData) {
|
|
5493
5746
|
const classList = slideData.classList || [];
|
|
5494
5747
|
for (const cls of classList) {
|
|
@@ -5600,34 +5853,44 @@ var VisualQualityEvaluator = class {
|
|
|
5600
5853
|
evaluateContentQuality(slideScores) {
|
|
5601
5854
|
let score = 25;
|
|
5602
5855
|
const notes = [];
|
|
5603
|
-
const
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5856
|
+
const criticalFailureSlides = slideScores.filter((s) => s.hasCriticalFailure);
|
|
5857
|
+
if (criticalFailureSlides.length > 0) {
|
|
5858
|
+
score = Math.max(0, 25 - criticalFailureSlides.length * 8);
|
|
5859
|
+
notes.push(`CRITICAL: ${criticalFailureSlides.length} slide(s) have critical failures`);
|
|
5860
|
+
criticalFailureSlides.forEach((s) => {
|
|
5861
|
+
s.criticalFailures.forEach((f) => notes.push(` - Slide ${s.slideIndex}: ${f}`));
|
|
5862
|
+
});
|
|
5609
5863
|
}
|
|
5610
|
-
const
|
|
5611
|
-
const
|
|
5864
|
+
const avgGlance = slideScores.reduce((sum, s) => sum + s.glanceTest, 0) / slideScores.length;
|
|
5865
|
+
const passesGlanceTest = avgGlance >= 6;
|
|
5866
|
+
if (!passesGlanceTest) {
|
|
5867
|
+
score -= 5;
|
|
5868
|
+
notes.push(`Glance Test: Average ${avgGlance.toFixed(1)}/10 - too text-heavy`);
|
|
5869
|
+
}
|
|
5870
|
+
const avgOneIdea = slideScores.reduce((sum, s) => sum + s.oneIdea, 0) / slideScores.length;
|
|
5871
|
+
const hasOneIdeaPerSlide = avgOneIdea >= 6;
|
|
5872
|
+
if (!hasOneIdeaPerSlide) {
|
|
5873
|
+
score -= 5;
|
|
5874
|
+
notes.push(`One Idea Rule: Average ${avgOneIdea.toFixed(1)}/10 - messages diluted`);
|
|
5875
|
+
}
|
|
5876
|
+
const messagesAreClear = avgOneIdea >= 7;
|
|
5877
|
+
const lowScoreSlides = slideScores.filter((s) => s.totalScore < 20).length;
|
|
5878
|
+
const appropriateDepth = lowScoreSlides < slideScores.length * 0.2;
|
|
5612
5879
|
if (!appropriateDepth) {
|
|
5613
5880
|
score -= 5;
|
|
5614
|
-
notes.push("Some slides have
|
|
5881
|
+
notes.push("Some slides have quality issues");
|
|
5615
5882
|
}
|
|
5616
|
-
const overloadedSlides = slideScores.filter(
|
|
5617
|
-
(s) => s.contentClarityNotes.includes("too long") || s.visualImpactNotes.includes("Too much")
|
|
5618
|
-
).length;
|
|
5883
|
+
const overloadedSlides = slideScores.filter((s) => s.glanceTest < 5).length;
|
|
5619
5884
|
const noOverload = overloadedSlides === 0;
|
|
5620
5885
|
if (!noOverload) {
|
|
5621
|
-
score -=
|
|
5622
|
-
notes.push(`${overloadedSlides} slides
|
|
5886
|
+
score -= 3;
|
|
5887
|
+
notes.push(`${overloadedSlides} slides fail glance test - too dense`);
|
|
5623
5888
|
}
|
|
5624
|
-
const
|
|
5625
|
-
|
|
5626
|
-
).length;
|
|
5627
|
-
const actionableInsights = insightSlides >= slideScores.length * 0.3;
|
|
5889
|
+
const excellentSlides = slideScores.filter((s) => s.totalScore >= 30).length;
|
|
5890
|
+
const actionableInsights = excellentSlides >= slideScores.length * 0.3;
|
|
5628
5891
|
if (!actionableInsights) {
|
|
5629
|
-
score -=
|
|
5630
|
-
notes.push("Need more high-impact
|
|
5892
|
+
score -= 3;
|
|
5893
|
+
notes.push("Need more high-impact slides");
|
|
5631
5894
|
}
|
|
5632
5895
|
return {
|
|
5633
5896
|
score: Math.max(0, score),
|
|
@@ -5641,30 +5904,50 @@ var VisualQualityEvaluator = class {
|
|
|
5641
5904
|
evaluateExecutiveReadiness(slideScores) {
|
|
5642
5905
|
let score = 25;
|
|
5643
5906
|
const notes = [];
|
|
5644
|
-
const
|
|
5645
|
-
|
|
5646
|
-
|
|
5907
|
+
const criticalFailureSlides = slideScores.filter((s) => s.hasCriticalFailure);
|
|
5908
|
+
if (criticalFailureSlides.length > 0) {
|
|
5909
|
+
score = 0;
|
|
5910
|
+
notes.push(`CRITICAL: ${criticalFailureSlides.length} slide(s) have critical failures - CANNOT show to executives`);
|
|
5911
|
+
criticalFailureSlides.forEach((s) => {
|
|
5912
|
+
notes.push(` - Slide ${s.slideIndex} (${s.slideType}): ${s.criticalFailures[0]}`);
|
|
5913
|
+
});
|
|
5914
|
+
return {
|
|
5915
|
+
score: 0,
|
|
5916
|
+
wouldImpress: false,
|
|
5917
|
+
readyForBoardroom: false,
|
|
5918
|
+
compelling: false,
|
|
5919
|
+
shareworthy: false,
|
|
5920
|
+
notes: notes.join(". ")
|
|
5921
|
+
};
|
|
5922
|
+
}
|
|
5923
|
+
const avgGlance = slideScores.reduce((sum, s) => sum + s.glanceTest, 0) / slideScores.length;
|
|
5924
|
+
const avgOneIdea = slideScores.reduce((sum, s) => sum + s.oneIdea, 0) / slideScores.length;
|
|
5925
|
+
const avgDataInk = slideScores.reduce((sum, s) => sum + s.dataInkRatio, 0) / slideScores.length;
|
|
5926
|
+
const avgExecution = slideScores.reduce((sum, s) => sum + s.professionalExecution, 0) / slideScores.length;
|
|
5647
5927
|
const avgTotal = slideScores.reduce((sum, s) => sum + s.totalScore, 0) / slideScores.length;
|
|
5648
|
-
const wouldImpress =
|
|
5928
|
+
const wouldImpress = avgGlance >= 7 && avgOneIdea >= 7 && avgExecution >= 7;
|
|
5649
5929
|
if (!wouldImpress) {
|
|
5650
5930
|
score -= 7;
|
|
5651
5931
|
notes.push("Needs more visual impact to impress");
|
|
5652
5932
|
}
|
|
5653
|
-
const readyForBoardroom =
|
|
5933
|
+
const readyForBoardroom = avgExecution >= 6 && avgDataInk >= 6;
|
|
5654
5934
|
if (!readyForBoardroom) {
|
|
5655
5935
|
score -= 7;
|
|
5656
|
-
notes.push(
|
|
5936
|
+
notes.push(`Boardroom readiness: Execution ${avgExecution.toFixed(1)}/10, Data-Ink ${avgDataInk.toFixed(1)}/10`);
|
|
5657
5937
|
}
|
|
5658
|
-
const compelling =
|
|
5938
|
+
const compelling = avgGlance >= 6 && avgOneIdea >= 6;
|
|
5659
5939
|
if (!compelling) {
|
|
5660
5940
|
score -= 5;
|
|
5661
|
-
notes.push(
|
|
5941
|
+
notes.push(`Compelling: Glance ${avgGlance.toFixed(1)}/10, OneIdea ${avgOneIdea.toFixed(1)}/10`);
|
|
5662
5942
|
}
|
|
5663
5943
|
const excellentSlides = slideScores.filter((s) => s.totalScore >= 30).length;
|
|
5664
5944
|
const shareworthy = excellentSlides >= slideScores.length * 0.4;
|
|
5665
5945
|
if (!shareworthy) {
|
|
5666
5946
|
score -= 5;
|
|
5667
|
-
notes.push(
|
|
5947
|
+
notes.push(`Shareworthy: ${excellentSlides}/${slideScores.length} excellent slides (need 40%)`);
|
|
5948
|
+
}
|
|
5949
|
+
if (wouldImpress && readyForBoardroom && compelling) {
|
|
5950
|
+
notes.push("Meets expert standards - McKinsey/TED quality");
|
|
5668
5951
|
}
|
|
5669
5952
|
return {
|
|
5670
5953
|
score: Math.max(0, score),
|