claude-presentation-master 7.2.0 → 7.3.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 +6 -0
- package/dist/index.d.mts +105 -11
- package/dist/index.d.ts +105 -11
- package/dist/index.js +612 -83
- package/dist/index.mjs +612 -83
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -102,6 +102,75 @@ var import_fs = require("fs");
|
|
|
102
102
|
var import_path = require("path");
|
|
103
103
|
var import_url = require("url");
|
|
104
104
|
var yaml = __toESM(require("yaml"));
|
|
105
|
+
|
|
106
|
+
// src/utils/Logger.ts
|
|
107
|
+
var Logger = class {
|
|
108
|
+
level;
|
|
109
|
+
prefix;
|
|
110
|
+
timestamps;
|
|
111
|
+
constructor(options = {}) {
|
|
112
|
+
this.level = options.level ?? 1 /* INFO */;
|
|
113
|
+
this.prefix = options.prefix ?? "";
|
|
114
|
+
this.timestamps = options.timestamps ?? false;
|
|
115
|
+
}
|
|
116
|
+
setLevel(level) {
|
|
117
|
+
this.level = level;
|
|
118
|
+
}
|
|
119
|
+
formatMessage(message) {
|
|
120
|
+
const parts = [];
|
|
121
|
+
if (this.timestamps) {
|
|
122
|
+
parts.push(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
|
|
123
|
+
}
|
|
124
|
+
if (this.prefix) {
|
|
125
|
+
parts.push(`[${this.prefix}]`);
|
|
126
|
+
}
|
|
127
|
+
parts.push(message);
|
|
128
|
+
return parts.join(" ");
|
|
129
|
+
}
|
|
130
|
+
debug(message, ...args) {
|
|
131
|
+
if (this.level <= 0 /* DEBUG */) {
|
|
132
|
+
console.debug(this.formatMessage(message), ...args);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
info(message, ...args) {
|
|
136
|
+
if (this.level <= 1 /* INFO */) {
|
|
137
|
+
console.info(this.formatMessage(message), ...args);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
warn(message, ...args) {
|
|
141
|
+
if (this.level <= 2 /* WARN */) {
|
|
142
|
+
console.warn(this.formatMessage(message), ...args);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
error(message, ...args) {
|
|
146
|
+
if (this.level <= 3 /* ERROR */) {
|
|
147
|
+
console.error(this.formatMessage(message), ...args);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Progress messages (always shown unless silent)
|
|
151
|
+
progress(message) {
|
|
152
|
+
if (this.level < 4 /* SILENT */) {
|
|
153
|
+
console.info(message);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Success messages
|
|
157
|
+
success(message) {
|
|
158
|
+
if (this.level < 4 /* SILENT */) {
|
|
159
|
+
console.info(`\u2705 ${message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Step messages for workflow progress
|
|
163
|
+
step(message) {
|
|
164
|
+
if (this.level <= 1 /* INFO */) {
|
|
165
|
+
console.info(` \u2713 ${message}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
var logger = new Logger({
|
|
170
|
+
level: process.env.LOG_LEVEL ? parseInt(process.env.LOG_LEVEL) : 1 /* INFO */
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// src/kb/KnowledgeGateway.ts
|
|
105
174
|
var import_meta = {};
|
|
106
175
|
function getModuleDir() {
|
|
107
176
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
@@ -113,7 +182,7 @@ function getModuleDir() {
|
|
|
113
182
|
return process.cwd();
|
|
114
183
|
}
|
|
115
184
|
var KnowledgeGateway = class {
|
|
116
|
-
kb;
|
|
185
|
+
kb = {};
|
|
117
186
|
loaded = false;
|
|
118
187
|
constructor() {
|
|
119
188
|
}
|
|
@@ -136,7 +205,7 @@ var KnowledgeGateway = class {
|
|
|
136
205
|
const content = (0, import_fs.readFileSync)(path, "utf-8");
|
|
137
206
|
this.kb = yaml.parse(content);
|
|
138
207
|
this.loaded = true;
|
|
139
|
-
|
|
208
|
+
logger.step(`Knowledge base loaded from ${path}`);
|
|
140
209
|
return;
|
|
141
210
|
} catch {
|
|
142
211
|
}
|
|
@@ -327,7 +396,7 @@ var KnowledgeGateway = class {
|
|
|
327
396
|
*/
|
|
328
397
|
getChartGuidance(purpose) {
|
|
329
398
|
this.ensureLoaded();
|
|
330
|
-
return this.kb.chart_selection_guide.by_purpose[purpose];
|
|
399
|
+
return this.kb.chart_selection_guide.by_purpose?.[purpose];
|
|
331
400
|
}
|
|
332
401
|
/**
|
|
333
402
|
* Get charts to avoid
|
|
@@ -342,7 +411,7 @@ var KnowledgeGateway = class {
|
|
|
342
411
|
*/
|
|
343
412
|
getIBPitchBookStructure(type) {
|
|
344
413
|
this.ensureLoaded();
|
|
345
|
-
return this.kb.investment_banking?.pitch_book_types[type];
|
|
414
|
+
return this.kb.investment_banking?.pitch_book_types?.[type];
|
|
346
415
|
}
|
|
347
416
|
/**
|
|
348
417
|
* Check if knowledge base is loaded
|
|
@@ -892,22 +961,27 @@ var ContentAnalyzer = class {
|
|
|
892
961
|
async analyze(content, contentType) {
|
|
893
962
|
await this.initialize();
|
|
894
963
|
const text = this.parseContent(content, contentType);
|
|
895
|
-
const title = this.
|
|
964
|
+
const { title, subtitle } = this.extractTitleAndSubtitle(text);
|
|
965
|
+
logger.step(`Title: "${title}", Subtitle: "${subtitle || "(none)"}"`);
|
|
896
966
|
const detectedType = this.detectPresentationType(text);
|
|
897
|
-
|
|
967
|
+
logger.step(`Detected type: ${detectedType}`);
|
|
898
968
|
const sections = this.extractSections(text);
|
|
899
|
-
|
|
969
|
+
logger.step(`Found ${sections.length} sections`);
|
|
900
970
|
const scqa = this.extractSCQA(text);
|
|
901
971
|
const sparkline = this.extractSparkline(text);
|
|
902
972
|
const keyMessages = this.extractKeyMessages(text);
|
|
903
973
|
const dataPoints = this.extractDataPoints(text);
|
|
904
|
-
|
|
974
|
+
logger.step(`Found ${dataPoints.length} data points`);
|
|
905
975
|
const titles = sections.map((s) => s.header).filter((h) => h.length > 0);
|
|
906
976
|
const starMoments = this.extractStarMoments(text);
|
|
907
977
|
const estimatedSlideCount = Math.max(5, sections.length + 3);
|
|
978
|
+
const whatIsContent = scqa.situation ? [scqa.situation] : [];
|
|
979
|
+
const whatCouldBeContent = scqa.answer ? [scqa.answer] : keyMessages.slice(0, 2);
|
|
908
980
|
return {
|
|
909
981
|
detectedType,
|
|
910
982
|
title,
|
|
983
|
+
subtitle,
|
|
984
|
+
// NEW: Use explicit subtitle from content
|
|
911
985
|
sections,
|
|
912
986
|
keyMessages,
|
|
913
987
|
dataPoints,
|
|
@@ -918,8 +992,10 @@ var ContentAnalyzer = class {
|
|
|
918
992
|
answer: scqa.answer || ""
|
|
919
993
|
},
|
|
920
994
|
sparkline: {
|
|
921
|
-
whatIs:
|
|
922
|
-
|
|
995
|
+
whatIs: whatIsContent,
|
|
996
|
+
// FIX: Use situation, not CTA
|
|
997
|
+
whatCouldBe: whatCouldBeContent,
|
|
998
|
+
// FIX: Use answer/vision, not random messages
|
|
923
999
|
callToAdventure: sparkline.callToAction || ""
|
|
924
1000
|
},
|
|
925
1001
|
titles,
|
|
@@ -947,18 +1023,46 @@ var ContentAnalyzer = class {
|
|
|
947
1023
|
return text;
|
|
948
1024
|
}
|
|
949
1025
|
/**
|
|
950
|
-
* Extract the main title from content
|
|
1026
|
+
* Extract the main title AND subtitle from content
|
|
1027
|
+
* Subtitle is the line immediately after the H1 title (if not another header)
|
|
951
1028
|
*/
|
|
952
|
-
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
1029
|
+
extractTitleAndSubtitle(text) {
|
|
1030
|
+
const lines = text.split("\n");
|
|
1031
|
+
let title = "Presentation";
|
|
1032
|
+
let subtitle = "";
|
|
1033
|
+
let foundTitle = false;
|
|
1034
|
+
let titleLineIndex = -1;
|
|
1035
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1036
|
+
const rawLine = lines[i];
|
|
1037
|
+
if (!rawLine) continue;
|
|
1038
|
+
const line = rawLine.trim();
|
|
1039
|
+
const h1Match = line.match(/^#\s+(.+)$/);
|
|
1040
|
+
if (h1Match && h1Match[1] && !foundTitle) {
|
|
1041
|
+
title = h1Match[1].replace(/\*\*/g, "").trim();
|
|
1042
|
+
foundTitle = true;
|
|
1043
|
+
titleLineIndex = i;
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (foundTitle && i > titleLineIndex && line.length > 0) {
|
|
1047
|
+
if (line.startsWith("#")) break;
|
|
1048
|
+
if (line.startsWith("-") || line.startsWith("*") || /^\d+\./.test(line)) break;
|
|
1049
|
+
subtitle = line.replace(/\*\*/g, "").trim().slice(0, 100);
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
956
1052
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1053
|
+
if (!foundTitle) {
|
|
1054
|
+
const firstLine = lines.find((l) => l.trim().length > 0);
|
|
1055
|
+
if (firstLine) {
|
|
1056
|
+
title = firstLine.replace(/^#+\s*/, "").replace(/\*\*/g, "").trim().slice(0, 80);
|
|
1057
|
+
}
|
|
960
1058
|
}
|
|
961
|
-
return
|
|
1059
|
+
return { title, subtitle };
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Extract the main title from content (legacy wrapper)
|
|
1063
|
+
*/
|
|
1064
|
+
extractTitle(text) {
|
|
1065
|
+
return this.extractTitleAndSubtitle(text).title;
|
|
962
1066
|
}
|
|
963
1067
|
/**
|
|
964
1068
|
* Detect presentation type from content signals
|
|
@@ -1021,12 +1125,16 @@ var ContentAnalyzer = class {
|
|
|
1021
1125
|
}
|
|
1022
1126
|
const bulletMatch = trimmedLine.match(/^[-*+]\s+(.+)$/);
|
|
1023
1127
|
if (bulletMatch && bulletMatch[1] && currentSection) {
|
|
1024
|
-
|
|
1128
|
+
const bulletText = bulletMatch[1];
|
|
1129
|
+
currentSection.bullets.push(bulletText);
|
|
1130
|
+
this.extractMetricsFromText(bulletText, currentSection.metrics);
|
|
1025
1131
|
continue;
|
|
1026
1132
|
}
|
|
1027
1133
|
const numberedMatch = trimmedLine.match(/^\d+\.\s+(.+)$/);
|
|
1028
1134
|
if (numberedMatch && numberedMatch[1] && currentSection) {
|
|
1029
|
-
|
|
1135
|
+
const itemText = numberedMatch[1];
|
|
1136
|
+
currentSection.bullets.push(itemText);
|
|
1137
|
+
this.extractMetricsFromText(itemText, currentSection.metrics);
|
|
1030
1138
|
continue;
|
|
1031
1139
|
}
|
|
1032
1140
|
const metricMatch = trimmedLine.match(/\$?([\d,]+\.?\d*)[%]?\s*[-–—:]\s*(.+)/);
|
|
@@ -1037,6 +1145,9 @@ var ContentAnalyzer = class {
|
|
|
1037
1145
|
});
|
|
1038
1146
|
continue;
|
|
1039
1147
|
}
|
|
1148
|
+
if (currentSection && trimmedLine) {
|
|
1149
|
+
this.extractMetricsFromText(trimmedLine, currentSection.metrics);
|
|
1150
|
+
}
|
|
1040
1151
|
if (trimmedLine.includes("|") && currentSection) {
|
|
1041
1152
|
const cells = trimmedLine.split("|").map((c) => c.trim()).filter((c) => c && !c.match(/^-+$/));
|
|
1042
1153
|
if (cells.length >= 2) {
|
|
@@ -1408,6 +1519,49 @@ var ContentAnalyzer = class {
|
|
|
1408
1519
|
const fallback = cleaned.slice(0, 150);
|
|
1409
1520
|
return fallback.length >= 20 ? fallback : "";
|
|
1410
1521
|
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Extract metrics from natural language text
|
|
1524
|
+
* Handles formats like "40% productivity loss", "$2.5M investment"
|
|
1525
|
+
*/
|
|
1526
|
+
extractMetricsFromText(text, metrics) {
|
|
1527
|
+
const usedValues = new Set(metrics.map((m) => m.value));
|
|
1528
|
+
const moneyMatches = text.matchAll(/(\$[\d,.]+[MBK]?)\s+([a-zA-Z][a-zA-Z\s]{2,30})/g);
|
|
1529
|
+
for (const match of moneyMatches) {
|
|
1530
|
+
const value = match[1];
|
|
1531
|
+
const rawLabel = match[2]?.trim();
|
|
1532
|
+
if (value && rawLabel && !usedValues.has(value)) {
|
|
1533
|
+
usedValues.add(value);
|
|
1534
|
+
metrics.push({ value, label: this.cleanMetricLabel(rawLabel) });
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
const percentMatches = text.matchAll(/(\d+(?:\.\d+)?%)\s+([a-zA-Z][a-zA-Z\s]{2,30})/g);
|
|
1538
|
+
for (const match of percentMatches) {
|
|
1539
|
+
const value = match[1];
|
|
1540
|
+
const rawLabel = match[2]?.trim();
|
|
1541
|
+
if (value && rawLabel && !usedValues.has(value)) {
|
|
1542
|
+
usedValues.add(value);
|
|
1543
|
+
metrics.push({ value, label: this.cleanMetricLabel(rawLabel) });
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
const multMatches = text.matchAll(/(\d+[xX])\s+([a-zA-Z][a-zA-Z\s]{2,20})/g);
|
|
1547
|
+
for (const match of multMatches) {
|
|
1548
|
+
const value = match[1];
|
|
1549
|
+
const rawLabel = match[2]?.trim();
|
|
1550
|
+
if (value && rawLabel && !usedValues.has(value)) {
|
|
1551
|
+
usedValues.add(value);
|
|
1552
|
+
metrics.push({ value, label: this.cleanMetricLabel(rawLabel) });
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
const numMatches = text.matchAll(/(\d{3,}\+?)\s+([a-zA-Z][a-zA-Z\s]{2,20})/g);
|
|
1556
|
+
for (const match of numMatches) {
|
|
1557
|
+
const value = match[1];
|
|
1558
|
+
const rawLabel = match[2]?.trim();
|
|
1559
|
+
if (value && rawLabel && !usedValues.has(value)) {
|
|
1560
|
+
usedValues.add(value);
|
|
1561
|
+
metrics.push({ value, label: this.cleanMetricLabel(rawLabel) });
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1411
1565
|
};
|
|
1412
1566
|
|
|
1413
1567
|
// src/core/ContentPatternClassifier.ts
|
|
@@ -1592,8 +1746,8 @@ var SlideFactory = class {
|
|
|
1592
1746
|
this.usedContent = /* @__PURE__ */ new Set();
|
|
1593
1747
|
this.usedTitles = /* @__PURE__ */ new Set();
|
|
1594
1748
|
this.config = this.loadKBConfig(type);
|
|
1595
|
-
|
|
1596
|
-
|
|
1749
|
+
logger.step(`SlideFactory v7.1.0 initialized for ${type} (${this.config.mode} mode)`);
|
|
1750
|
+
logger.step(`KB Config loaded: ${this.config.allowedTypes.length} allowed types`);
|
|
1597
1751
|
}
|
|
1598
1752
|
/**
|
|
1599
1753
|
* Load ALL configuration from KB - ZERO hardcoded values after this.
|
|
@@ -1621,7 +1775,7 @@ var SlideFactory = class {
|
|
|
1621
1775
|
async createSlides(analysis) {
|
|
1622
1776
|
const slides = [];
|
|
1623
1777
|
const storyStructure = this.kb.getStoryStructure(this.presentationType);
|
|
1624
|
-
|
|
1778
|
+
logger.step(`Using ${storyStructure.framework} story framework`);
|
|
1625
1779
|
slides.push(this.createTitleSlide(0, analysis));
|
|
1626
1780
|
const minSectionsForAgenda = Math.max(3, this.config.rules.bulletsPerSlide.max - 2);
|
|
1627
1781
|
if (this.config.mode === "business" && analysis.sections.length >= minSectionsForAgenda) {
|
|
@@ -1659,7 +1813,7 @@ var SlideFactory = class {
|
|
|
1659
1813
|
const validatedSlides = this.validateAndFixSlides(slides);
|
|
1660
1814
|
this.checkRequiredElements(validatedSlides);
|
|
1661
1815
|
this.checkAntiPatterns(validatedSlides);
|
|
1662
|
-
|
|
1816
|
+
logger.step(`Created ${validatedSlides.length} validated slides`);
|
|
1663
1817
|
return validatedSlides;
|
|
1664
1818
|
}
|
|
1665
1819
|
// ===========================================================================
|
|
@@ -1700,7 +1854,7 @@ var SlideFactory = class {
|
|
|
1700
1854
|
case "technical":
|
|
1701
1855
|
return this.createCodeSlide(index, section);
|
|
1702
1856
|
default:
|
|
1703
|
-
|
|
1857
|
+
logger.warn(`Unknown slide type '${type}', using bullet-points`);
|
|
1704
1858
|
return this.createBulletSlide(index, section);
|
|
1705
1859
|
}
|
|
1706
1860
|
}
|
|
@@ -1711,9 +1865,10 @@ var SlideFactory = class {
|
|
|
1711
1865
|
const data = {
|
|
1712
1866
|
title: this.truncateText(analysis.title, this.config.rules.wordsPerSlide.max)
|
|
1713
1867
|
};
|
|
1714
|
-
|
|
1868
|
+
const subtitleSource = analysis.subtitle || analysis.keyMessages[0] || "";
|
|
1869
|
+
if (subtitleSource) {
|
|
1715
1870
|
data.subtitle = this.truncateText(
|
|
1716
|
-
|
|
1871
|
+
subtitleSource,
|
|
1717
1872
|
this.config.defaults.subtitle.maxWords
|
|
1718
1873
|
// FROM KB
|
|
1719
1874
|
);
|
|
@@ -1757,7 +1912,9 @@ var SlideFactory = class {
|
|
|
1757
1912
|
addSCQASlides(slides, analysis) {
|
|
1758
1913
|
const scqa = analysis.scqa;
|
|
1759
1914
|
const titles = this.config.scqaTitles;
|
|
1760
|
-
|
|
1915
|
+
const minBodyLength = 20;
|
|
1916
|
+
const situationBody = scqa?.situation ? this.truncateText(scqa.situation, this.config.rules.wordsPerSlide.max) : "";
|
|
1917
|
+
if (situationBody.length >= minBodyLength && !this.usedContent.has("scqa-situation")) {
|
|
1761
1918
|
this.usedContent.add("scqa-situation");
|
|
1762
1919
|
slides.push({
|
|
1763
1920
|
index: slides.length,
|
|
@@ -1765,12 +1922,13 @@ var SlideFactory = class {
|
|
|
1765
1922
|
data: {
|
|
1766
1923
|
title: titles.situation,
|
|
1767
1924
|
// FROM KB - not hardcoded 'Current Situation'
|
|
1768
|
-
body:
|
|
1925
|
+
body: situationBody
|
|
1769
1926
|
},
|
|
1770
1927
|
classes: ["situation-slide"]
|
|
1771
1928
|
});
|
|
1772
1929
|
}
|
|
1773
|
-
|
|
1930
|
+
const complicationBody = scqa?.complication ? this.truncateText(scqa.complication, this.config.rules.wordsPerSlide.max) : "";
|
|
1931
|
+
if (complicationBody.length >= minBodyLength && !this.usedContent.has("scqa-complication")) {
|
|
1774
1932
|
this.usedContent.add("scqa-complication");
|
|
1775
1933
|
slides.push({
|
|
1776
1934
|
index: slides.length,
|
|
@@ -1778,12 +1936,13 @@ var SlideFactory = class {
|
|
|
1778
1936
|
data: {
|
|
1779
1937
|
title: titles.complication,
|
|
1780
1938
|
// FROM KB - not hardcoded 'The Challenge'
|
|
1781
|
-
body:
|
|
1939
|
+
body: complicationBody
|
|
1782
1940
|
},
|
|
1783
1941
|
classes: ["complication-slide"]
|
|
1784
1942
|
});
|
|
1785
1943
|
}
|
|
1786
|
-
|
|
1944
|
+
const questionBody = scqa?.question ? this.truncateText(scqa.question, this.config.rules.wordsPerSlide.max) : "";
|
|
1945
|
+
if (questionBody.length >= minBodyLength && !this.usedContent.has("scqa-question")) {
|
|
1787
1946
|
this.usedContent.add("scqa-question");
|
|
1788
1947
|
slides.push({
|
|
1789
1948
|
index: slides.length,
|
|
@@ -1791,7 +1950,7 @@ var SlideFactory = class {
|
|
|
1791
1950
|
data: {
|
|
1792
1951
|
title: titles.question,
|
|
1793
1952
|
// FROM KB - not hardcoded 'The Question'
|
|
1794
|
-
body:
|
|
1953
|
+
body: questionBody
|
|
1795
1954
|
},
|
|
1796
1955
|
classes: ["question-slide"]
|
|
1797
1956
|
});
|
|
@@ -1801,7 +1960,8 @@ var SlideFactory = class {
|
|
|
1801
1960
|
const spark = analysis.sparkline;
|
|
1802
1961
|
const titles = this.config.sparklineTitles;
|
|
1803
1962
|
const whatIsFirst = spark?.whatIs?.[0];
|
|
1804
|
-
|
|
1963
|
+
const whatIsBody = whatIsFirst ? this.truncateText(whatIsFirst, this.config.rules.wordsPerSlide.max) : "";
|
|
1964
|
+
if (whatIsBody.length >= 20 && !this.usedContent.has("spark-what-is")) {
|
|
1805
1965
|
this.usedContent.add("spark-what-is");
|
|
1806
1966
|
slides.push({
|
|
1807
1967
|
index: slides.length,
|
|
@@ -1809,13 +1969,14 @@ var SlideFactory = class {
|
|
|
1809
1969
|
data: {
|
|
1810
1970
|
title: titles.whatIs,
|
|
1811
1971
|
// FROM KB - not hardcoded 'Where We Are Today'
|
|
1812
|
-
body:
|
|
1972
|
+
body: whatIsBody
|
|
1813
1973
|
},
|
|
1814
1974
|
classes: ["what-is-slide"]
|
|
1815
1975
|
});
|
|
1816
1976
|
}
|
|
1817
1977
|
const whatCouldBeFirst = spark?.whatCouldBe?.[0];
|
|
1818
|
-
|
|
1978
|
+
const whatCouldBeBody = whatCouldBeFirst ? this.truncateText(whatCouldBeFirst, this.config.rules.wordsPerSlide.max) : "";
|
|
1979
|
+
if (whatCouldBeBody.length >= 20 && !this.usedContent.has("spark-could-be")) {
|
|
1819
1980
|
this.usedContent.add("spark-could-be");
|
|
1820
1981
|
slides.push({
|
|
1821
1982
|
index: slides.length,
|
|
@@ -1823,7 +1984,7 @@ var SlideFactory = class {
|
|
|
1823
1984
|
data: {
|
|
1824
1985
|
title: titles.whatCouldBe,
|
|
1825
1986
|
// FROM KB - not hardcoded 'What Could Be'
|
|
1826
|
-
body:
|
|
1987
|
+
body: whatCouldBeBody
|
|
1827
1988
|
},
|
|
1828
1989
|
classes: ["what-could-be-slide"]
|
|
1829
1990
|
});
|
|
@@ -1833,15 +1994,19 @@ var SlideFactory = class {
|
|
|
1833
1994
|
// CONTENT SLIDES - ALL values from KB
|
|
1834
1995
|
// ===========================================================================
|
|
1835
1996
|
createBigNumberSlide(index, section, pattern) {
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
|
|
1997
|
+
const fullContent = `${section.header} ${section.content} ${section.bullets.join(" ")}`;
|
|
1998
|
+
const bigNumber = this.classifier.extractBigNumber(fullContent);
|
|
1999
|
+
const actualValue = bigNumber?.value || pattern.bigNumberValue;
|
|
2000
|
+
if (!actualValue) {
|
|
2001
|
+
logger.warn(`No number found for big-number slide, falling back to single-statement`);
|
|
2002
|
+
return this.createSingleStatementSlide(index, section);
|
|
2003
|
+
}
|
|
1839
2004
|
return {
|
|
1840
2005
|
index,
|
|
1841
2006
|
type: "big-number",
|
|
1842
2007
|
data: {
|
|
1843
2008
|
title: this.createTitle(section.header, section),
|
|
1844
|
-
keyMessage:
|
|
2009
|
+
keyMessage: actualValue,
|
|
1845
2010
|
body: bigNumber?.context || this.truncateText(
|
|
1846
2011
|
section.content,
|
|
1847
2012
|
this.config.defaults.context.maxWords
|
|
@@ -2028,16 +2193,18 @@ var SlideFactory = class {
|
|
|
2028
2193
|
};
|
|
2029
2194
|
}
|
|
2030
2195
|
createSingleStatementSlide(index, section) {
|
|
2196
|
+
const bodyContent = section.content || section.bullets.join(" ") || "";
|
|
2197
|
+
const truncatedBody = this.truncateText(bodyContent, this.config.rules.wordsPerSlide.max);
|
|
2198
|
+
if (truncatedBody.length < 15 && section.bullets.length > 0) {
|
|
2199
|
+
return this.createBulletSlide(index, section);
|
|
2200
|
+
}
|
|
2031
2201
|
return {
|
|
2032
2202
|
index,
|
|
2033
2203
|
type: "single-statement",
|
|
2034
2204
|
data: {
|
|
2035
2205
|
title: this.createTitle(section.header, section),
|
|
2036
|
-
body:
|
|
2037
|
-
|
|
2038
|
-
this.config.rules.wordsPerSlide.max
|
|
2039
|
-
// FROM KB
|
|
2040
|
-
)
|
|
2206
|
+
body: truncatedBody || section.header
|
|
2207
|
+
// Fallback to header if no body
|
|
2041
2208
|
},
|
|
2042
2209
|
classes: ["single-statement-slide"]
|
|
2043
2210
|
};
|
|
@@ -2110,7 +2277,7 @@ var SlideFactory = class {
|
|
|
2110
2277
|
slide.data.bullets = slide.data.bullets.slice(0, validation.fixes.bulletCount);
|
|
2111
2278
|
}
|
|
2112
2279
|
if (validation.violations.length > 0) {
|
|
2113
|
-
|
|
2280
|
+
logger.warn(`Slide ${slide.index} (${slide.type}): Fixed ${Object.keys(validation.fixes).length} issues, ${validation.violations.length} remaining`);
|
|
2114
2281
|
}
|
|
2115
2282
|
if (validation.warnings.length > 0) {
|
|
2116
2283
|
slide.notes = (slide.notes || "") + "\nWarnings: " + validation.warnings.join(", ");
|
|
@@ -2150,7 +2317,7 @@ var SlideFactory = class {
|
|
|
2150
2317
|
}
|
|
2151
2318
|
}
|
|
2152
2319
|
if (missingElements.length > 0) {
|
|
2153
|
-
|
|
2320
|
+
logger.warn(`Missing required elements: ${missingElements.join(", ")}`);
|
|
2154
2321
|
}
|
|
2155
2322
|
}
|
|
2156
2323
|
/**
|
|
@@ -2181,7 +2348,7 @@ var SlideFactory = class {
|
|
|
2181
2348
|
}
|
|
2182
2349
|
}
|
|
2183
2350
|
if (violations.length > 0) {
|
|
2184
|
-
|
|
2351
|
+
logger.warn(`Anti-pattern violations: ${violations.join(", ")}`);
|
|
2185
2352
|
}
|
|
2186
2353
|
}
|
|
2187
2354
|
// ===========================================================================
|
|
@@ -2526,9 +2693,12 @@ var TemplateEngine = class {
|
|
|
2526
2693
|
this.templates.set("big-number", this.handlebars.compile(`
|
|
2527
2694
|
<section class="{{classes}}" data-slide-index="{{slideIndex}}" style="{{styles}}">
|
|
2528
2695
|
<div class="slide-content big-number-content">
|
|
2529
|
-
|
|
2530
|
-
{{
|
|
2531
|
-
|
|
2696
|
+
{{#if title}}
|
|
2697
|
+
<h2 class="title animate-fadeIn">{{title}}</h2>
|
|
2698
|
+
{{/if}}
|
|
2699
|
+
<div class="number animate-zoomIn">{{keyMessage}}</div>
|
|
2700
|
+
{{#if body}}
|
|
2701
|
+
<p class="number-context animate-fadeIn delay-300">{{body}}</p>
|
|
2532
2702
|
{{/if}}
|
|
2533
2703
|
{{> source}}
|
|
2534
2704
|
</div>
|
|
@@ -4912,6 +5082,12 @@ ${slides}
|
|
|
4912
5082
|
const highlight = palette.accent || "#e94560";
|
|
4913
5083
|
const text = palette.text || "#1a1a2e";
|
|
4914
5084
|
const background = palette.background || "#ffffff";
|
|
5085
|
+
const isDark = this.isDarkPalette(palette);
|
|
5086
|
+
const defaultBg = isDark ? background : "#ffffff";
|
|
5087
|
+
const defaultText = isDark ? text : "#1a1a2e";
|
|
5088
|
+
const headingColor = isDark ? primary : primary;
|
|
5089
|
+
const titleBg = isDark ? background : primary;
|
|
5090
|
+
const titleBgEnd = isDark ? this.lightenColor(background, 10) : this.darkenColor(primary, 15);
|
|
4915
5091
|
return `
|
|
4916
5092
|
/* Base Styles - KB-Driven Design System */
|
|
4917
5093
|
:root {
|
|
@@ -4938,7 +5114,13 @@ ${slides}
|
|
|
4938
5114
|
font-family: var(--font-body);
|
|
4939
5115
|
font-size: ${fontSize};
|
|
4940
5116
|
line-height: ${lineHeight};
|
|
4941
|
-
color:
|
|
5117
|
+
color: ${defaultText};
|
|
5118
|
+
background: ${defaultBg};
|
|
5119
|
+
}
|
|
5120
|
+
|
|
5121
|
+
/* Override reveal.js default backgrounds for dark mode */
|
|
5122
|
+
.reveal .slides section {
|
|
5123
|
+
background: ${defaultBg};
|
|
4942
5124
|
}
|
|
4943
5125
|
|
|
4944
5126
|
.reveal .slides {
|
|
@@ -4967,10 +5149,17 @@ ${slides}
|
|
|
4967
5149
|
font-family: var(--font-heading);
|
|
4968
5150
|
font-weight: 700;
|
|
4969
5151
|
letter-spacing: -0.02em;
|
|
4970
|
-
color:
|
|
5152
|
+
color: ${isDark ? primary : primary};
|
|
4971
5153
|
margin-bottom: 0.5em;
|
|
4972
5154
|
}
|
|
4973
5155
|
|
|
5156
|
+
/* Dark mode text visibility */
|
|
5157
|
+
${isDark ? `
|
|
5158
|
+
.reveal, .reveal p, .reveal li, .reveal span {
|
|
5159
|
+
color: ${text};
|
|
5160
|
+
}
|
|
5161
|
+
` : ""}
|
|
5162
|
+
|
|
4974
5163
|
.reveal h1 { font-size: 2.5em; }
|
|
4975
5164
|
.reveal h2 { font-size: 1.8em; }
|
|
4976
5165
|
.reveal h3 { font-size: 1.3em; }
|
|
@@ -5148,6 +5337,124 @@ ${slides}
|
|
|
5148
5337
|
font-weight: 600;
|
|
5149
5338
|
margin-top: 1em;
|
|
5150
5339
|
}
|
|
5340
|
+
|
|
5341
|
+
/* ================================================================
|
|
5342
|
+
SLIDE TYPE BACKGROUNDS - KB-Driven Visual Design
|
|
5343
|
+
Creates visual hierarchy and differentiation between slide types
|
|
5344
|
+
================================================================ */
|
|
5345
|
+
|
|
5346
|
+
/* Title slide: Bold background - makes strong first impression */
|
|
5347
|
+
.reveal .slide-title {
|
|
5348
|
+
background: linear-gradient(135deg, ${titleBg} 0%, ${titleBgEnd} 100%);
|
|
5349
|
+
}
|
|
5350
|
+
.reveal .slide-title h1,
|
|
5351
|
+
.reveal .slide-title h2,
|
|
5352
|
+
.reveal .slide-title p,
|
|
5353
|
+
.reveal .slide-title .subtitle {
|
|
5354
|
+
color: ${isDark ? primary : "#ffffff"};
|
|
5355
|
+
}
|
|
5356
|
+
|
|
5357
|
+
/* Section dividers: Subtle visual breaks */
|
|
5358
|
+
.reveal .slide-section-divider {
|
|
5359
|
+
background: ${isDark ? this.lightenColor(background, 8) : `linear-gradient(180deg, ${this.lightenColor(primary, 85)} 0%, ${this.lightenColor(primary, 92)} 100%)`};
|
|
5360
|
+
}
|
|
5361
|
+
|
|
5362
|
+
/* Big number slides: Clean with accent highlight - emphasizes the data */
|
|
5363
|
+
.reveal .slide-big-number {
|
|
5364
|
+
background: var(--color-background);
|
|
5365
|
+
border-left: 8px solid var(--color-highlight);
|
|
5366
|
+
}
|
|
5367
|
+
.reveal .slide-big-number .number {
|
|
5368
|
+
font-size: 5em;
|
|
5369
|
+
background: linear-gradient(135deg, var(--color-highlight) 0%, var(--color-accent) 100%);
|
|
5370
|
+
-webkit-background-clip: text;
|
|
5371
|
+
-webkit-text-fill-color: transparent;
|
|
5372
|
+
background-clip: text;
|
|
5373
|
+
}
|
|
5374
|
+
|
|
5375
|
+
/* Metrics grid: Light accent background - data-focused */
|
|
5376
|
+
.reveal .slide-metrics-grid {
|
|
5377
|
+
background: ${isDark ? this.lightenColor(background, 8) : this.lightenColor(accent, 90)};
|
|
5378
|
+
}
|
|
5379
|
+
${isDark ? `.reveal .slide-metrics-grid { color: ${text}; }` : ""}
|
|
5380
|
+
|
|
5381
|
+
/* CTA slide: Highlight color - drives action */
|
|
5382
|
+
.reveal .slide-cta {
|
|
5383
|
+
background: linear-gradient(135deg, var(--color-highlight) 0%, var(--color-accent) 100%);
|
|
5384
|
+
}
|
|
5385
|
+
.reveal .slide-cta h2,
|
|
5386
|
+
.reveal .slide-cta p {
|
|
5387
|
+
color: #ffffff;
|
|
5388
|
+
}
|
|
5389
|
+
.reveal .slide-cta .cta-button {
|
|
5390
|
+
background: #ffffff;
|
|
5391
|
+
color: var(--color-highlight);
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5394
|
+
/* Thank you slide: Matches title for bookend effect */
|
|
5395
|
+
.reveal .slide-thank-you {
|
|
5396
|
+
background: linear-gradient(135deg, ${titleBg} 0%, ${titleBgEnd} 100%);
|
|
5397
|
+
}
|
|
5398
|
+
.reveal .slide-thank-you h2,
|
|
5399
|
+
.reveal .slide-thank-you p,
|
|
5400
|
+
.reveal .slide-thank-you .subtitle {
|
|
5401
|
+
color: ${isDark ? primary : "#ffffff"};
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5404
|
+
/* Quote slides: Elegant subtle background */
|
|
5405
|
+
.reveal .slide-quote {
|
|
5406
|
+
background: ${isDark ? this.lightenColor(background, 5) : this.lightenColor(secondary, 92)};
|
|
5407
|
+
}
|
|
5408
|
+
.reveal .slide-quote blockquote {
|
|
5409
|
+
border-left-color: var(--color-accent);
|
|
5410
|
+
font-size: 1.3em;
|
|
5411
|
+
}
|
|
5412
|
+
${isDark ? `.reveal .slide-quote { color: ${text}; }` : ""}
|
|
5413
|
+
|
|
5414
|
+
/* Single statement: Clean, centered, impactful */
|
|
5415
|
+
.reveal .slide-single-statement {
|
|
5416
|
+
background: var(--color-background);
|
|
5417
|
+
}
|
|
5418
|
+
.reveal .slide-single-statement .statement,
|
|
5419
|
+
.reveal .slide-single-statement .big-idea-text {
|
|
5420
|
+
font-size: 2.2em;
|
|
5421
|
+
max-width: 80%;
|
|
5422
|
+
margin: 0 auto;
|
|
5423
|
+
}
|
|
5424
|
+
|
|
5425
|
+
/* Comparison slides: Split visual */
|
|
5426
|
+
.reveal .slide-comparison .columns {
|
|
5427
|
+
gap: 60px;
|
|
5428
|
+
}
|
|
5429
|
+
.reveal .slide-comparison .column:first-child {
|
|
5430
|
+
border-right: 2px solid ${this.lightenColor(secondary, 70)};
|
|
5431
|
+
padding-right: 30px;
|
|
5432
|
+
}
|
|
5433
|
+
|
|
5434
|
+
/* Timeline/Process: Visual flow */
|
|
5435
|
+
.reveal .slide-timeline .steps,
|
|
5436
|
+
.reveal .slide-process .steps {
|
|
5437
|
+
display: flex;
|
|
5438
|
+
gap: 20px;
|
|
5439
|
+
}
|
|
5440
|
+
.reveal .slide-timeline .step,
|
|
5441
|
+
.reveal .slide-process .step {
|
|
5442
|
+
flex: 1;
|
|
5443
|
+
padding: 20px;
|
|
5444
|
+
background: ${isDark ? this.lightenColor(background, 10) : this.lightenColor(secondary, 92)};
|
|
5445
|
+
border-radius: 8px;
|
|
5446
|
+
border-top: 4px solid var(--color-accent);
|
|
5447
|
+
${isDark ? `color: ${text};` : ""}
|
|
5448
|
+
}
|
|
5449
|
+
|
|
5450
|
+
/* Progress bar enhancement */
|
|
5451
|
+
.reveal .progress {
|
|
5452
|
+
background: ${this.lightenColor(primary, 80)};
|
|
5453
|
+
height: 4px;
|
|
5454
|
+
}
|
|
5455
|
+
.reveal .progress span {
|
|
5456
|
+
background: var(--color-highlight);
|
|
5457
|
+
}
|
|
5151
5458
|
`;
|
|
5152
5459
|
}
|
|
5153
5460
|
/**
|
|
@@ -5163,6 +5470,37 @@ ${slides}
|
|
|
5163
5470
|
b = Math.min(255, Math.floor(b + (255 - b) * (percent / 100)));
|
|
5164
5471
|
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
5165
5472
|
}
|
|
5473
|
+
/**
|
|
5474
|
+
* Check if a palette is "dark mode" (dark background, light text)
|
|
5475
|
+
*/
|
|
5476
|
+
isDarkPalette(palette) {
|
|
5477
|
+
const bgLuminance = this.getLuminance(palette.background || "#ffffff");
|
|
5478
|
+
const textLuminance = this.getLuminance(palette.text || "#000000");
|
|
5479
|
+
return bgLuminance < textLuminance;
|
|
5480
|
+
}
|
|
5481
|
+
/**
|
|
5482
|
+
* Get relative luminance of a hex color (0-1, 0=black, 1=white)
|
|
5483
|
+
*/
|
|
5484
|
+
getLuminance(hex) {
|
|
5485
|
+
hex = hex.replace("#", "");
|
|
5486
|
+
const r = parseInt(hex.substring(0, 2), 16) / 255;
|
|
5487
|
+
const g = parseInt(hex.substring(2, 4), 16) / 255;
|
|
5488
|
+
const b = parseInt(hex.substring(4, 6), 16) / 255;
|
|
5489
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
5490
|
+
}
|
|
5491
|
+
/**
|
|
5492
|
+
* Darken a hex color
|
|
5493
|
+
*/
|
|
5494
|
+
darkenColor(hex, percent) {
|
|
5495
|
+
hex = hex.replace("#", "");
|
|
5496
|
+
let r = parseInt(hex.substring(0, 2), 16);
|
|
5497
|
+
let g = parseInt(hex.substring(2, 4), 16);
|
|
5498
|
+
let b = parseInt(hex.substring(4, 6), 16);
|
|
5499
|
+
r = Math.max(0, Math.floor(r * (1 - percent / 100)));
|
|
5500
|
+
g = Math.max(0, Math.floor(g * (1 - percent / 100)));
|
|
5501
|
+
b = Math.max(0, Math.floor(b * (1 - percent / 100)));
|
|
5502
|
+
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
5503
|
+
}
|
|
5166
5504
|
/**
|
|
5167
5505
|
* Get theme-specific styles - KB-DRIVEN
|
|
5168
5506
|
*/
|
|
@@ -5379,15 +5717,15 @@ var IterativeQAEngine = class {
|
|
|
5379
5717
|
let autoFixSummary = "";
|
|
5380
5718
|
let totalFixesApplied = 0;
|
|
5381
5719
|
if (opts.verbose) {
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5720
|
+
logger.progress("\n\u{1F504} Starting Iterative QA Process");
|
|
5721
|
+
logger.info(` Target Score: ${opts.minScore}/100`);
|
|
5722
|
+
logger.info(` Max Iterations: ${opts.maxIterations}`);
|
|
5723
|
+
logger.progress("");
|
|
5386
5724
|
}
|
|
5387
5725
|
for (let i = 0; i < opts.maxIterations; i++) {
|
|
5388
5726
|
const iterationNum = i + 1;
|
|
5389
5727
|
if (opts.verbose) {
|
|
5390
|
-
|
|
5728
|
+
logger.progress(`\u{1F4CA} Iteration ${iterationNum}/${opts.maxIterations}...`);
|
|
5391
5729
|
}
|
|
5392
5730
|
scoringResult = await this.scorer.score(currentSlides, currentHtml, opts.minScore);
|
|
5393
5731
|
iterations.push({
|
|
@@ -5406,17 +5744,17 @@ var IterativeQAEngine = class {
|
|
|
5406
5744
|
timestamp: /* @__PURE__ */ new Date()
|
|
5407
5745
|
});
|
|
5408
5746
|
if (opts.verbose) {
|
|
5409
|
-
|
|
5747
|
+
logger.info(` Score: ${scoringResult.overallScore}/100`);
|
|
5410
5748
|
}
|
|
5411
5749
|
if (scoringResult.passed) {
|
|
5412
5750
|
if (opts.verbose) {
|
|
5413
|
-
|
|
5751
|
+
logger.success(`PASSED - Score meets threshold (${opts.minScore})`);
|
|
5414
5752
|
}
|
|
5415
5753
|
break;
|
|
5416
5754
|
}
|
|
5417
5755
|
if (i < opts.maxIterations - 1) {
|
|
5418
5756
|
if (opts.verbose) {
|
|
5419
|
-
|
|
5757
|
+
logger.warn("Below threshold, applying auto-fixes...");
|
|
5420
5758
|
}
|
|
5421
5759
|
const fixResult = await this.fixer.fix(currentSlides, scoringResult);
|
|
5422
5760
|
if (fixResult.fixesApplied.length > 0) {
|
|
@@ -5428,12 +5766,12 @@ var IterativeQAEngine = class {
|
|
|
5428
5766
|
}
|
|
5429
5767
|
totalFixesApplied += fixResult.fixesApplied.length;
|
|
5430
5768
|
if (opts.verbose) {
|
|
5431
|
-
|
|
5769
|
+
logger.info(` \u{1F527} Applied ${fixResult.fixesApplied.length} fixes`);
|
|
5432
5770
|
}
|
|
5433
5771
|
autoFixSummary += fixResult.summary + "\n";
|
|
5434
5772
|
} else {
|
|
5435
5773
|
if (opts.verbose) {
|
|
5436
|
-
|
|
5774
|
+
logger.warn("No auto-fixes available, manual review needed");
|
|
5437
5775
|
}
|
|
5438
5776
|
break;
|
|
5439
5777
|
}
|
|
@@ -5449,7 +5787,7 @@ var IterativeQAEngine = class {
|
|
|
5449
5787
|
totalFixesApplied
|
|
5450
5788
|
);
|
|
5451
5789
|
if (opts.verbose) {
|
|
5452
|
-
|
|
5790
|
+
logger.info(report);
|
|
5453
5791
|
}
|
|
5454
5792
|
return {
|
|
5455
5793
|
finalScore: scoringResult.overallScore,
|
|
@@ -6251,6 +6589,172 @@ var PowerPointGenerator = class {
|
|
|
6251
6589
|
}
|
|
6252
6590
|
};
|
|
6253
6591
|
|
|
6592
|
+
// src/generators/PDFGenerator.ts
|
|
6593
|
+
var import_puppeteer = __toESM(require("puppeteer"));
|
|
6594
|
+
var PDFGenerator = class {
|
|
6595
|
+
defaultOptions = {
|
|
6596
|
+
orientation: "landscape",
|
|
6597
|
+
printBackground: true,
|
|
6598
|
+
format: "Slide",
|
|
6599
|
+
scale: 1
|
|
6600
|
+
};
|
|
6601
|
+
/**
|
|
6602
|
+
* Generate PDF from HTML presentation
|
|
6603
|
+
* @param html - The HTML content of the presentation
|
|
6604
|
+
* @param options - PDF generation options
|
|
6605
|
+
* @returns Buffer containing the PDF file
|
|
6606
|
+
*/
|
|
6607
|
+
async generate(html, options = {}) {
|
|
6608
|
+
const opts = { ...this.defaultOptions, ...options };
|
|
6609
|
+
logger.progress("\u{1F4C4} Generating PDF...");
|
|
6610
|
+
let browser;
|
|
6611
|
+
try {
|
|
6612
|
+
browser = await import_puppeteer.default.launch({
|
|
6613
|
+
headless: true,
|
|
6614
|
+
args: [
|
|
6615
|
+
"--no-sandbox",
|
|
6616
|
+
"--disable-setuid-sandbox",
|
|
6617
|
+
"--disable-dev-shm-usage"
|
|
6618
|
+
]
|
|
6619
|
+
});
|
|
6620
|
+
const page = await browser.newPage();
|
|
6621
|
+
const printHtml = this.preparePrintHtml(html);
|
|
6622
|
+
await page.setViewport({
|
|
6623
|
+
width: 1920,
|
|
6624
|
+
height: 1080
|
|
6625
|
+
});
|
|
6626
|
+
await page.setContent(printHtml, {
|
|
6627
|
+
waitUntil: ["networkidle0", "domcontentloaded"]
|
|
6628
|
+
});
|
|
6629
|
+
await page.evaluate(() => {
|
|
6630
|
+
return new Promise((resolve) => {
|
|
6631
|
+
if (document.fonts && document.fonts.ready) {
|
|
6632
|
+
document.fonts.ready.then(() => resolve());
|
|
6633
|
+
} else {
|
|
6634
|
+
setTimeout(resolve, 1e3);
|
|
6635
|
+
}
|
|
6636
|
+
});
|
|
6637
|
+
});
|
|
6638
|
+
const pdfBuffer = await page.pdf({
|
|
6639
|
+
width: "1920px",
|
|
6640
|
+
height: "1080px",
|
|
6641
|
+
printBackground: opts.printBackground,
|
|
6642
|
+
scale: opts.scale,
|
|
6643
|
+
landscape: opts.orientation === "landscape",
|
|
6644
|
+
margin: {
|
|
6645
|
+
top: "0px",
|
|
6646
|
+
right: "0px",
|
|
6647
|
+
bottom: "0px",
|
|
6648
|
+
left: "0px"
|
|
6649
|
+
}
|
|
6650
|
+
});
|
|
6651
|
+
logger.step("PDF generated successfully");
|
|
6652
|
+
return Buffer.from(pdfBuffer);
|
|
6653
|
+
} catch (error) {
|
|
6654
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6655
|
+
logger.error(`PDF generation failed: ${errorMessage}`);
|
|
6656
|
+
throw new Error(`PDF generation failed: ${errorMessage}`);
|
|
6657
|
+
} finally {
|
|
6658
|
+
if (browser) {
|
|
6659
|
+
await browser.close();
|
|
6660
|
+
}
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
/**
|
|
6664
|
+
* Prepare HTML for print/PDF output
|
|
6665
|
+
* Modifies the HTML to work better as a printed document
|
|
6666
|
+
*/
|
|
6667
|
+
preparePrintHtml(html) {
|
|
6668
|
+
const printCss = `
|
|
6669
|
+
<style id="pdf-print-styles">
|
|
6670
|
+
/* Print-specific overrides */
|
|
6671
|
+
@page {
|
|
6672
|
+
size: 1920px 1080px;
|
|
6673
|
+
margin: 0;
|
|
6674
|
+
}
|
|
6675
|
+
|
|
6676
|
+
@media print {
|
|
6677
|
+
html, body {
|
|
6678
|
+
margin: 0;
|
|
6679
|
+
padding: 0;
|
|
6680
|
+
width: 1920px;
|
|
6681
|
+
height: 1080px;
|
|
6682
|
+
}
|
|
6683
|
+
|
|
6684
|
+
.reveal .slides {
|
|
6685
|
+
width: 100% !important;
|
|
6686
|
+
height: 100% !important;
|
|
6687
|
+
margin: 0 !important;
|
|
6688
|
+
padding: 0 !important;
|
|
6689
|
+
transform: none !important;
|
|
6690
|
+
}
|
|
6691
|
+
|
|
6692
|
+
.reveal .slides section {
|
|
6693
|
+
page-break-after: always;
|
|
6694
|
+
page-break-inside: avoid;
|
|
6695
|
+
width: 1920px !important;
|
|
6696
|
+
height: 1080px !important;
|
|
6697
|
+
margin: 0 !important;
|
|
6698
|
+
padding: 60px !important;
|
|
6699
|
+
box-sizing: border-box;
|
|
6700
|
+
position: relative !important;
|
|
6701
|
+
top: auto !important;
|
|
6702
|
+
left: auto !important;
|
|
6703
|
+
transform: none !important;
|
|
6704
|
+
display: flex !important;
|
|
6705
|
+
opacity: 1 !important;
|
|
6706
|
+
visibility: visible !important;
|
|
6707
|
+
}
|
|
6708
|
+
|
|
6709
|
+
.reveal .slides section:last-child {
|
|
6710
|
+
page-break-after: auto;
|
|
6711
|
+
}
|
|
6712
|
+
|
|
6713
|
+
/* Make all slides visible for printing */
|
|
6714
|
+
.reveal .slides section.future,
|
|
6715
|
+
.reveal .slides section.past {
|
|
6716
|
+
display: flex !important;
|
|
6717
|
+
opacity: 1 !important;
|
|
6718
|
+
visibility: visible !important;
|
|
6719
|
+
}
|
|
6720
|
+
|
|
6721
|
+
/* Disable animations for print */
|
|
6722
|
+
* {
|
|
6723
|
+
animation: none !important;
|
|
6724
|
+
transition: none !important;
|
|
6725
|
+
}
|
|
6726
|
+
|
|
6727
|
+
/* Hide Reveal.js controls */
|
|
6728
|
+
.reveal .controls,
|
|
6729
|
+
.reveal .progress,
|
|
6730
|
+
.reveal .slide-number,
|
|
6731
|
+
.reveal .speaker-notes,
|
|
6732
|
+
.reveal .navigate-left,
|
|
6733
|
+
.reveal .navigate-right,
|
|
6734
|
+
.reveal .navigate-up,
|
|
6735
|
+
.reveal .navigate-down {
|
|
6736
|
+
display: none !important;
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
|
|
6740
|
+
/* Force print mode in Puppeteer */
|
|
6741
|
+
.reveal .slides section {
|
|
6742
|
+
page-break-after: always;
|
|
6743
|
+
page-break-inside: avoid;
|
|
6744
|
+
}
|
|
6745
|
+
</style>
|
|
6746
|
+
`;
|
|
6747
|
+
if (html.includes("</head>")) {
|
|
6748
|
+
return html.replace("</head>", `${printCss}</head>`);
|
|
6749
|
+
} else {
|
|
6750
|
+
return printCss + html;
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
};
|
|
6754
|
+
function createPDFGenerator() {
|
|
6755
|
+
return new PDFGenerator();
|
|
6756
|
+
}
|
|
6757
|
+
|
|
6254
6758
|
// src/core/PresentationEngine.ts
|
|
6255
6759
|
var PresentationEngine = class {
|
|
6256
6760
|
contentAnalyzer;
|
|
@@ -6259,6 +6763,7 @@ var PresentationEngine = class {
|
|
|
6259
6763
|
qaEngine;
|
|
6260
6764
|
htmlGenerator;
|
|
6261
6765
|
pptxGenerator;
|
|
6766
|
+
pdfGenerator;
|
|
6262
6767
|
kb;
|
|
6263
6768
|
constructor() {
|
|
6264
6769
|
this.contentAnalyzer = new ContentAnalyzer();
|
|
@@ -6267,6 +6772,7 @@ var PresentationEngine = class {
|
|
|
6267
6772
|
this.qaEngine = new QAEngine();
|
|
6268
6773
|
this.htmlGenerator = new RevealJsGenerator();
|
|
6269
6774
|
this.pptxGenerator = new PowerPointGenerator();
|
|
6775
|
+
this.pdfGenerator = createPDFGenerator();
|
|
6270
6776
|
}
|
|
6271
6777
|
/**
|
|
6272
6778
|
* Generate a presentation from content.
|
|
@@ -6276,26 +6782,26 @@ var PresentationEngine = class {
|
|
|
6276
6782
|
*/
|
|
6277
6783
|
async generate(config) {
|
|
6278
6784
|
this.validateConfig(config);
|
|
6279
|
-
|
|
6785
|
+
logger.progress("\u{1F4DA} Loading knowledge base...");
|
|
6280
6786
|
this.kb = await getKnowledgeGateway();
|
|
6281
|
-
|
|
6787
|
+
logger.progress("\u{1F4DD} Analyzing content...");
|
|
6282
6788
|
const analysis = await this.contentAnalyzer.analyze(config.content, config.contentType);
|
|
6283
6789
|
const presentationType = config.presentationType || analysis.detectedType;
|
|
6284
|
-
|
|
6790
|
+
logger.step(`Presentation type: ${presentationType}`);
|
|
6285
6791
|
const slideFactory = createSlideFactory(this.kb, presentationType);
|
|
6286
|
-
|
|
6792
|
+
logger.progress("\u{1F3A8} Creating slides...");
|
|
6287
6793
|
const slides = await slideFactory.createSlides(analysis);
|
|
6288
|
-
|
|
6794
|
+
logger.progress("\u2705 Validating structure...");
|
|
6289
6795
|
const structureErrors = this.validateStructure(slides, config.mode);
|
|
6290
6796
|
if (structureErrors.length > 0) {
|
|
6291
6797
|
if (config.skipQA) {
|
|
6292
|
-
|
|
6293
|
-
structureErrors.forEach((e) =>
|
|
6798
|
+
logger.warn("Structure warnings (bypassed):");
|
|
6799
|
+
structureErrors.forEach((e) => logger.warn(` \u2022 ${e}`));
|
|
6294
6800
|
} else {
|
|
6295
6801
|
throw new ValidationError(structureErrors, "Slide structure validation failed");
|
|
6296
6802
|
}
|
|
6297
6803
|
}
|
|
6298
|
-
|
|
6804
|
+
logger.progress("\u{1F528} Generating outputs...");
|
|
6299
6805
|
const outputs = {};
|
|
6300
6806
|
if (config.format.includes("html")) {
|
|
6301
6807
|
outputs.html = await this.htmlGenerator.generate(slides, config);
|
|
@@ -6313,7 +6819,7 @@ var PresentationEngine = class {
|
|
|
6313
6819
|
const maxIterations = config.maxIterations ?? 5;
|
|
6314
6820
|
const useIterativeQA = config.useIterativeQA !== false;
|
|
6315
6821
|
if (useIterativeQA) {
|
|
6316
|
-
|
|
6822
|
+
logger.progress("\u{1F50D} Running Iterative QA (7-Dimension Scoring)...");
|
|
6317
6823
|
const iterativeEngine = createIterativeQAEngine(
|
|
6318
6824
|
config.mode,
|
|
6319
6825
|
presentationType,
|
|
@@ -6335,20 +6841,30 @@ var PresentationEngine = class {
|
|
|
6335
6841
|
throw new QAFailureError(score, threshold, qaResults);
|
|
6336
6842
|
}
|
|
6337
6843
|
} else {
|
|
6338
|
-
|
|
6844
|
+
logger.progress("\u{1F50D} Running QA validation (legacy mode)...");
|
|
6339
6845
|
qaResults = await this.qaEngine.validate(outputs.html, {
|
|
6340
6846
|
mode: config.mode,
|
|
6341
6847
|
strictMode: true
|
|
6342
6848
|
});
|
|
6343
6849
|
score = this.scoreCalculator.calculate(qaResults);
|
|
6344
|
-
|
|
6850
|
+
logger.info(`\u{1F4CA} QA Score: ${score}/100`);
|
|
6345
6851
|
if (score < threshold) {
|
|
6346
6852
|
throw new QAFailureError(score, threshold, qaResults);
|
|
6347
6853
|
}
|
|
6348
6854
|
}
|
|
6349
6855
|
} else {
|
|
6350
6856
|
qaResults = this.qaEngine.createEmptyResults();
|
|
6351
|
-
|
|
6857
|
+
logger.warn("QA validation skipped (NOT RECOMMENDED)");
|
|
6858
|
+
}
|
|
6859
|
+
if (outputs.html && (config.format.includes("pdf") || config.generatePdf !== false)) {
|
|
6860
|
+
try {
|
|
6861
|
+
logger.progress("\u{1F4C4} Generating PDF from validated HTML...");
|
|
6862
|
+
outputs.pdf = await this.pdfGenerator.generate(outputs.html);
|
|
6863
|
+
logger.step("PDF generated successfully");
|
|
6864
|
+
} catch (pdfError) {
|
|
6865
|
+
const errorMsg = pdfError instanceof Error ? pdfError.message : String(pdfError);
|
|
6866
|
+
logger.warn(`PDF generation failed (non-critical): ${errorMsg}`);
|
|
6867
|
+
}
|
|
6352
6868
|
}
|
|
6353
6869
|
const metadata = this.buildMetadata(config, analysis, finalSlides, iterativeResult);
|
|
6354
6870
|
return {
|
|
@@ -6436,15 +6952,28 @@ var PresentationEngine = class {
|
|
|
6436
6952
|
if (slides.length < 3) {
|
|
6437
6953
|
errors.push("Presentation must have at least 3 slides");
|
|
6438
6954
|
}
|
|
6955
|
+
const sparseByDesignTypes = [
|
|
6956
|
+
"title",
|
|
6957
|
+
"section-divider",
|
|
6958
|
+
"thank-you",
|
|
6959
|
+
"big-number",
|
|
6960
|
+
"single-statement",
|
|
6961
|
+
"cta",
|
|
6962
|
+
"agenda",
|
|
6963
|
+
"metrics-grid",
|
|
6964
|
+
"quote",
|
|
6965
|
+
"image-focus"
|
|
6966
|
+
];
|
|
6439
6967
|
slides.forEach((slide, index) => {
|
|
6440
6968
|
const wordCount = this.countWords(slide);
|
|
6441
6969
|
if (mode === "keynote") {
|
|
6442
|
-
if (wordCount > 25) {
|
|
6970
|
+
if (wordCount > 25 && !sparseByDesignTypes.includes(slide.type)) {
|
|
6443
6971
|
errors.push(`Slide ${index + 1}: ${wordCount} words exceeds keynote limit of 25`);
|
|
6444
6972
|
}
|
|
6445
6973
|
} else {
|
|
6446
|
-
|
|
6447
|
-
|
|
6974
|
+
const requiresMinWords = ["bullet-points", "two-column", "process-flow"];
|
|
6975
|
+
if (wordCount < 20 && requiresMinWords.includes(slide.type)) {
|
|
6976
|
+
logger.warn(`Slide ${index + 1}: ${wordCount} words is sparse for ${slide.type} slide`);
|
|
6448
6977
|
}
|
|
6449
6978
|
if (wordCount > 100) {
|
|
6450
6979
|
errors.push(`Slide ${index + 1}: ${wordCount} words exceeds business limit of 100`);
|
|
@@ -6538,11 +7067,11 @@ var SlideGenerator = class {
|
|
|
6538
7067
|
await this.initialize();
|
|
6539
7068
|
this.presentationType = type || analysis.detectedType;
|
|
6540
7069
|
this.mode = this.kb.getModeForType(this.presentationType);
|
|
6541
|
-
|
|
7070
|
+
logger.step(`Using ${this.mode} mode for ${this.presentationType}`);
|
|
6542
7071
|
this.factory = createSlideFactory(this.kb, this.presentationType);
|
|
6543
7072
|
const slides = await this.factory.createSlides(analysis);
|
|
6544
7073
|
const legacySlides = this.convertToLegacyFormat(slides);
|
|
6545
|
-
|
|
7074
|
+
logger.step(`Generated ${legacySlides.length} KB-validated slides`);
|
|
6546
7075
|
return legacySlides;
|
|
6547
7076
|
}
|
|
6548
7077
|
/**
|