claude-presentation-master 8.1.0 → 8.2.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/dist/index.d.mts +30 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +240 -33
- package/dist/index.mjs +240 -33
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -127,6 +127,26 @@ interface QAResults {
|
|
|
127
127
|
passed: boolean;
|
|
128
128
|
/** List of issues found */
|
|
129
129
|
issues: QAIssue[];
|
|
130
|
+
/** Per-slide scores from visual QA */
|
|
131
|
+
slideScores?: SlideScore[];
|
|
132
|
+
/** Overall QA score */
|
|
133
|
+
score?: number;
|
|
134
|
+
/** QA status */
|
|
135
|
+
status?: string;
|
|
136
|
+
}
|
|
137
|
+
interface SlideScore {
|
|
138
|
+
/** Slide index */
|
|
139
|
+
index: number;
|
|
140
|
+
/** Slide type */
|
|
141
|
+
type: string;
|
|
142
|
+
/** Score 0-100 */
|
|
143
|
+
score: number;
|
|
144
|
+
/** Expert verdict */
|
|
145
|
+
expertVerdict?: string;
|
|
146
|
+
/** Critical issues found */
|
|
147
|
+
criticalIssues?: string[];
|
|
148
|
+
/** All issues */
|
|
149
|
+
issues?: QAIssue[];
|
|
130
150
|
}
|
|
131
151
|
interface VisualQAResults {
|
|
132
152
|
/** Whitespace percentage (target: 35%+ keynote, 25%+ business) */
|
|
@@ -391,19 +411,28 @@ interface SparklineStructure {
|
|
|
391
411
|
}
|
|
392
412
|
declare class ValidationError extends Error {
|
|
393
413
|
errors: string[];
|
|
414
|
+
readonly suggestions: string[];
|
|
394
415
|
constructor(errors: string[], message?: string);
|
|
416
|
+
private static getSuggestions;
|
|
395
417
|
}
|
|
396
418
|
declare class QAFailureError extends Error {
|
|
397
419
|
score: number;
|
|
398
420
|
threshold: number;
|
|
399
421
|
qaResults: QAResults;
|
|
422
|
+
readonly topIssues: string[];
|
|
400
423
|
constructor(score: number, threshold: number, qaResults: QAResults, message?: string);
|
|
401
424
|
getIssues(): string[];
|
|
425
|
+
private static getTopIssues;
|
|
402
426
|
}
|
|
403
427
|
declare class TemplateNotFoundError extends Error {
|
|
404
428
|
templatePath: string;
|
|
405
429
|
constructor(templatePath: string, message?: string);
|
|
406
430
|
}
|
|
431
|
+
declare class KnowledgeBaseError extends Error {
|
|
432
|
+
field: string;
|
|
433
|
+
context: string;
|
|
434
|
+
constructor(field: string, context: string, message?: string);
|
|
435
|
+
}
|
|
407
436
|
/**
|
|
408
437
|
* Content pattern detected from a section
|
|
409
438
|
* Used to match content to appropriate slide types
|
|
@@ -2173,4 +2202,4 @@ declare const _default: {
|
|
|
2173
2202
|
VERSION: string;
|
|
2174
2203
|
};
|
|
2175
2204
|
|
|
2176
|
-
export { type AccessibilityResults, type ChartData, type ChartDataset, ChartJsProvider, type ChartProvider, type ChartRequest, type ChartResult, type ChartType, type ColorPalette, CompositeChartProvider, CompositeImageProvider, type ContentAnalysis$1 as ContentAnalysis, ContentAnalyzer, type ContentPattern, ContentPatternClassifier, type ContentQAResults, type ContentSection$1 as ContentSection, type ContrastIssue, type ExpertQAResults, type ExpertValidation, type FontSizeIssue, type GlanceTestResult, type ImageData, type ImageProvider, type ImageRequest, type ImageResult, KnowledgeGateway, type LegacySlide, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationQualityScore, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type ScoringWeights, type SevenDimensionQAResult, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, SlideGenerator, type SlideTemplate, type SlideType, type SlideValidationResult, type SlideVisualScore, type SparklineStructure, type StoryStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, type TypographyRules, UnsplashImageProvider, VERSION, ValidationError, type ValidationRules, type VisualQAResults, VisualQualityEvaluator, createDefaultChartProvider, createDefaultImageProvider, createSlideFactory, _default as default, evaluatePresentation, generate, getKnowledgeGateway, initSlideGenerator, validate };
|
|
2205
|
+
export { type AccessibilityResults, type ChartData, type ChartDataset, ChartJsProvider, type ChartProvider, type ChartRequest, type ChartResult, type ChartType, type ColorPalette, CompositeChartProvider, CompositeImageProvider, type ContentAnalysis$1 as ContentAnalysis, ContentAnalyzer, type ContentPattern, ContentPatternClassifier, type ContentQAResults, type ContentSection$1 as ContentSection, type ContrastIssue, type ExpertQAResults, type ExpertValidation, type FontSizeIssue, type GlanceTestResult, type ImageData, type ImageProvider, type ImageRequest, type ImageResult, KnowledgeBaseError, KnowledgeGateway, type LegacySlide, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationQualityScore, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type ScoringWeights, type SevenDimensionQAResult, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, SlideGenerator, type SlideScore, type SlideTemplate, type SlideType, type SlideValidationResult, type SlideVisualScore, type SparklineStructure, type StoryStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, type TypographyRules, UnsplashImageProvider, VERSION, ValidationError, type ValidationRules, type VisualQAResults, VisualQualityEvaluator, createDefaultChartProvider, createDefaultImageProvider, createSlideFactory, _default as default, evaluatePresentation, generate, getKnowledgeGateway, initSlideGenerator, validate };
|
package/dist/index.d.ts
CHANGED
|
@@ -127,6 +127,26 @@ interface QAResults {
|
|
|
127
127
|
passed: boolean;
|
|
128
128
|
/** List of issues found */
|
|
129
129
|
issues: QAIssue[];
|
|
130
|
+
/** Per-slide scores from visual QA */
|
|
131
|
+
slideScores?: SlideScore[];
|
|
132
|
+
/** Overall QA score */
|
|
133
|
+
score?: number;
|
|
134
|
+
/** QA status */
|
|
135
|
+
status?: string;
|
|
136
|
+
}
|
|
137
|
+
interface SlideScore {
|
|
138
|
+
/** Slide index */
|
|
139
|
+
index: number;
|
|
140
|
+
/** Slide type */
|
|
141
|
+
type: string;
|
|
142
|
+
/** Score 0-100 */
|
|
143
|
+
score: number;
|
|
144
|
+
/** Expert verdict */
|
|
145
|
+
expertVerdict?: string;
|
|
146
|
+
/** Critical issues found */
|
|
147
|
+
criticalIssues?: string[];
|
|
148
|
+
/** All issues */
|
|
149
|
+
issues?: QAIssue[];
|
|
130
150
|
}
|
|
131
151
|
interface VisualQAResults {
|
|
132
152
|
/** Whitespace percentage (target: 35%+ keynote, 25%+ business) */
|
|
@@ -391,19 +411,28 @@ interface SparklineStructure {
|
|
|
391
411
|
}
|
|
392
412
|
declare class ValidationError extends Error {
|
|
393
413
|
errors: string[];
|
|
414
|
+
readonly suggestions: string[];
|
|
394
415
|
constructor(errors: string[], message?: string);
|
|
416
|
+
private static getSuggestions;
|
|
395
417
|
}
|
|
396
418
|
declare class QAFailureError extends Error {
|
|
397
419
|
score: number;
|
|
398
420
|
threshold: number;
|
|
399
421
|
qaResults: QAResults;
|
|
422
|
+
readonly topIssues: string[];
|
|
400
423
|
constructor(score: number, threshold: number, qaResults: QAResults, message?: string);
|
|
401
424
|
getIssues(): string[];
|
|
425
|
+
private static getTopIssues;
|
|
402
426
|
}
|
|
403
427
|
declare class TemplateNotFoundError extends Error {
|
|
404
428
|
templatePath: string;
|
|
405
429
|
constructor(templatePath: string, message?: string);
|
|
406
430
|
}
|
|
431
|
+
declare class KnowledgeBaseError extends Error {
|
|
432
|
+
field: string;
|
|
433
|
+
context: string;
|
|
434
|
+
constructor(field: string, context: string, message?: string);
|
|
435
|
+
}
|
|
407
436
|
/**
|
|
408
437
|
* Content pattern detected from a section
|
|
409
438
|
* Used to match content to appropriate slide types
|
|
@@ -2173,4 +2202,4 @@ declare const _default: {
|
|
|
2173
2202
|
VERSION: string;
|
|
2174
2203
|
};
|
|
2175
2204
|
|
|
2176
|
-
export { type AccessibilityResults, type ChartData, type ChartDataset, ChartJsProvider, type ChartProvider, type ChartRequest, type ChartResult, type ChartType, type ColorPalette, CompositeChartProvider, CompositeImageProvider, type ContentAnalysis$1 as ContentAnalysis, ContentAnalyzer, type ContentPattern, ContentPatternClassifier, type ContentQAResults, type ContentSection$1 as ContentSection, type ContrastIssue, type ExpertQAResults, type ExpertValidation, type FontSizeIssue, type GlanceTestResult, type ImageData, type ImageProvider, type ImageRequest, type ImageResult, KnowledgeGateway, type LegacySlide, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationQualityScore, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type ScoringWeights, type SevenDimensionQAResult, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, SlideGenerator, type SlideTemplate, type SlideType, type SlideValidationResult, type SlideVisualScore, type SparklineStructure, type StoryStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, type TypographyRules, UnsplashImageProvider, VERSION, ValidationError, type ValidationRules, type VisualQAResults, VisualQualityEvaluator, createDefaultChartProvider, createDefaultImageProvider, createSlideFactory, _default as default, evaluatePresentation, generate, getKnowledgeGateway, initSlideGenerator, validate };
|
|
2205
|
+
export { type AccessibilityResults, type ChartData, type ChartDataset, ChartJsProvider, type ChartProvider, type ChartRequest, type ChartResult, type ChartType, type ColorPalette, CompositeChartProvider, CompositeImageProvider, type ContentAnalysis$1 as ContentAnalysis, ContentAnalyzer, type ContentPattern, ContentPatternClassifier, type ContentQAResults, type ContentSection$1 as ContentSection, type ContrastIssue, type ExpertQAResults, type ExpertValidation, type FontSizeIssue, type GlanceTestResult, type ImageData, type ImageProvider, type ImageRequest, type ImageResult, KnowledgeBaseError, KnowledgeGateway, type LegacySlide, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationQualityScore, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type ScoringWeights, type SevenDimensionQAResult, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, SlideGenerator, type SlideScore, type SlideTemplate, type SlideType, type SlideValidationResult, type SlideVisualScore, type SparklineStructure, type StoryStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, type TypographyRules, UnsplashImageProvider, VERSION, ValidationError, type ValidationRules, type VisualQAResults, VisualQualityEvaluator, createDefaultChartProvider, createDefaultImageProvider, createSlideFactory, _default as default, evaluatePresentation, generate, getKnowledgeGateway, initSlideGenerator, validate };
|
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
CompositeImageProvider: () => CompositeImageProvider,
|
|
36
36
|
ContentAnalyzer: () => ContentAnalyzer,
|
|
37
37
|
ContentPatternClassifier: () => ContentPatternClassifier,
|
|
38
|
+
KnowledgeBaseError: () => KnowledgeBaseError,
|
|
38
39
|
KnowledgeGateway: () => KnowledgeGateway,
|
|
39
40
|
LocalImageProvider: () => LocalImageProvider,
|
|
40
41
|
MermaidProvider: () => MermaidProvider,
|
|
@@ -67,32 +68,100 @@ __export(index_exports, {
|
|
|
67
68
|
module.exports = __toCommonJS(index_exports);
|
|
68
69
|
|
|
69
70
|
// src/types/index.ts
|
|
70
|
-
var ValidationError = class extends Error {
|
|
71
|
+
var ValidationError = class _ValidationError extends Error {
|
|
71
72
|
constructor(errors, message = "Validation failed") {
|
|
72
|
-
|
|
73
|
+
const errorList = errors.map((e) => ` \u2022 ${e}`).join("\n");
|
|
74
|
+
const suggestions = _ValidationError.getSuggestions(errors);
|
|
75
|
+
const suggestionList = suggestions.length > 0 ? "\n\nHow to fix:\n" + suggestions.map((s) => ` \u2192 ${s}`).join("\n") : "";
|
|
76
|
+
super(`${message}
|
|
77
|
+
|
|
78
|
+
Issues found:
|
|
79
|
+
${errorList}${suggestionList}`);
|
|
73
80
|
this.errors = errors;
|
|
74
81
|
this.name = "ValidationError";
|
|
82
|
+
this.suggestions = suggestions;
|
|
83
|
+
}
|
|
84
|
+
suggestions;
|
|
85
|
+
static getSuggestions(errors) {
|
|
86
|
+
const suggestions = [];
|
|
87
|
+
for (const error of errors) {
|
|
88
|
+
if (error.includes("Content is required")) {
|
|
89
|
+
suggestions.push("Provide markdown content with at least one ## section header");
|
|
90
|
+
}
|
|
91
|
+
if (error.includes("Mode must be")) {
|
|
92
|
+
suggestions.push('Use mode: "keynote" for TED-style or mode: "business" for corporate');
|
|
93
|
+
}
|
|
94
|
+
if (error.includes("Title is required")) {
|
|
95
|
+
suggestions.push('Add a title: "Your Presentation Title" to your config');
|
|
96
|
+
}
|
|
97
|
+
if (error.includes("output format")) {
|
|
98
|
+
suggestions.push('Specify format: ["html"] or format: ["html", "pdf"]');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return [...new Set(suggestions)];
|
|
75
102
|
}
|
|
76
103
|
};
|
|
77
|
-
var QAFailureError = class extends Error {
|
|
78
|
-
constructor(score, threshold, qaResults, message
|
|
79
|
-
|
|
104
|
+
var QAFailureError = class _QAFailureError extends Error {
|
|
105
|
+
constructor(score, threshold, qaResults, message) {
|
|
106
|
+
const topIssues = _QAFailureError.getTopIssues(qaResults);
|
|
107
|
+
const issueList = topIssues.map((i) => ` \u2022 ${i}`).join("\n");
|
|
108
|
+
const helpText = `
|
|
109
|
+
|
|
110
|
+
Score: ${score}/100 (threshold: ${threshold})
|
|
111
|
+
|
|
112
|
+
Top issues to fix:
|
|
113
|
+
${issueList}
|
|
114
|
+
|
|
115
|
+
How to improve:
|
|
116
|
+
\u2192 Ensure each slide has meaningful content (not just a title)
|
|
117
|
+
\u2192 Keep text concise - aim for <40 words per slide
|
|
118
|
+
\u2192 Use timeline/process slides for step-by-step content
|
|
119
|
+
\u2192 Add visuals for data-heavy slides`;
|
|
120
|
+
super(message || `QA score ${score} below threshold ${threshold}${helpText}`);
|
|
80
121
|
this.score = score;
|
|
81
122
|
this.threshold = threshold;
|
|
82
123
|
this.qaResults = qaResults;
|
|
83
124
|
this.name = "QAFailureError";
|
|
125
|
+
this.topIssues = topIssues;
|
|
84
126
|
}
|
|
127
|
+
topIssues;
|
|
85
128
|
getIssues() {
|
|
86
129
|
return this.qaResults.issues.map((issue) => issue.message);
|
|
87
130
|
}
|
|
131
|
+
static getTopIssues(qaResults) {
|
|
132
|
+
const issues = [];
|
|
133
|
+
if (qaResults.slideScores) {
|
|
134
|
+
for (const slide of qaResults.slideScores) {
|
|
135
|
+
if (slide.criticalIssues) {
|
|
136
|
+
issues.push(...slide.criticalIssues);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (qaResults.issues) {
|
|
141
|
+
issues.push(...qaResults.issues.map((i) => i.message));
|
|
142
|
+
}
|
|
143
|
+
return [...new Set(issues)].slice(0, 5);
|
|
144
|
+
}
|
|
88
145
|
};
|
|
89
146
|
var TemplateNotFoundError = class extends Error {
|
|
90
|
-
constructor(templatePath, message
|
|
91
|
-
super(message
|
|
147
|
+
constructor(templatePath, message) {
|
|
148
|
+
super(message || `Template not found: ${templatePath}
|
|
149
|
+
|
|
150
|
+
Available templates: title, content, timeline, process, metrics, quote, cta, thank-you`);
|
|
92
151
|
this.templatePath = templatePath;
|
|
93
152
|
this.name = "TemplateNotFoundError";
|
|
94
153
|
}
|
|
95
154
|
};
|
|
155
|
+
var KnowledgeBaseError = class extends Error {
|
|
156
|
+
constructor(field, context, message) {
|
|
157
|
+
super(message || `Knowledge base configuration error: Missing "${field}" in ${context}
|
|
158
|
+
|
|
159
|
+
Check assets/presentation-knowledge.yaml for the correct structure.`);
|
|
160
|
+
this.field = field;
|
|
161
|
+
this.context = context;
|
|
162
|
+
this.name = "KnowledgeBaseError";
|
|
163
|
+
}
|
|
164
|
+
};
|
|
96
165
|
|
|
97
166
|
// src/core/PresentationEngine.ts
|
|
98
167
|
var fs2 = __toESM(require("fs"));
|
|
@@ -102,7 +171,6 @@ var os = __toESM(require("os"));
|
|
|
102
171
|
// src/kb/KnowledgeGateway.ts
|
|
103
172
|
var import_fs = require("fs");
|
|
104
173
|
var import_path = require("path");
|
|
105
|
-
var import_url = require("url");
|
|
106
174
|
var yaml = __toESM(require("yaml"));
|
|
107
175
|
|
|
108
176
|
// src/utils/Logger.ts
|
|
@@ -173,11 +241,7 @@ var logger = new Logger({
|
|
|
173
241
|
});
|
|
174
242
|
|
|
175
243
|
// src/kb/KnowledgeGateway.ts
|
|
176
|
-
var import_meta = {};
|
|
177
244
|
function getModuleDir() {
|
|
178
|
-
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
179
|
-
return (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
180
|
-
}
|
|
181
245
|
if (typeof __dirname !== "undefined") {
|
|
182
246
|
return __dirname;
|
|
183
247
|
}
|
|
@@ -2867,25 +2931,43 @@ var SlideFactory = class {
|
|
|
2867
2931
|
createComparisonSlide(index, section) {
|
|
2868
2932
|
const comparison = this.classifier.extractComparison(section);
|
|
2869
2933
|
const labels = this.config.defaults.comparison;
|
|
2934
|
+
const title = this.createTitle(section.header, section);
|
|
2935
|
+
const normalizedTitle = title.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2870
2936
|
const leftFallback = labels.optionLabels[0] ?? "Option A";
|
|
2871
2937
|
const rightFallback = labels.optionLabels[1] ?? "Option B";
|
|
2872
|
-
|
|
2873
|
-
|
|
2938
|
+
let leftColumn = comparison?.left || section.bullets[0] || "";
|
|
2939
|
+
let rightColumn = comparison?.right || section.bullets[1] || "";
|
|
2940
|
+
const normalizedLeft = leftColumn.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2941
|
+
const normalizedRight = rightColumn.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2942
|
+
const leftIsDuplicate = normalizedLeft === normalizedTitle || normalizedTitle.includes(normalizedLeft);
|
|
2943
|
+
const rightIsDuplicate = normalizedRight === normalizedTitle || normalizedTitle.includes(normalizedRight);
|
|
2944
|
+
if (leftIsDuplicate || rightIsDuplicate || !leftColumn && !rightColumn) {
|
|
2945
|
+
if (section.bullets.length >= 2) {
|
|
2946
|
+
logger.warn(`Comparison slide "${title}" has duplicate content, using bullet points instead`);
|
|
2947
|
+
return this.createBulletSlide(index, section);
|
|
2948
|
+
}
|
|
2949
|
+
if (leftIsDuplicate && rightIsDuplicate) {
|
|
2950
|
+
logger.warn(`Skipping comparison slide "${title}" - content duplicates title`);
|
|
2951
|
+
return null;
|
|
2952
|
+
}
|
|
2953
|
+
if (leftIsDuplicate) leftColumn = leftFallback;
|
|
2954
|
+
if (rightIsDuplicate) rightColumn = rightFallback;
|
|
2955
|
+
}
|
|
2874
2956
|
return {
|
|
2875
2957
|
index,
|
|
2876
2958
|
type: "comparison",
|
|
2877
2959
|
data: {
|
|
2878
|
-
title
|
|
2960
|
+
title,
|
|
2879
2961
|
columns: [
|
|
2880
2962
|
{
|
|
2881
2963
|
title: labels.leftLabel,
|
|
2882
2964
|
// FROM KB - not hardcoded 'Before'
|
|
2883
|
-
content: this.truncateText(leftColumn, this.config.rules.wordsPerSlide.max)
|
|
2965
|
+
content: this.truncateText(leftColumn || leftFallback, this.config.rules.wordsPerSlide.max)
|
|
2884
2966
|
},
|
|
2885
2967
|
{
|
|
2886
2968
|
title: labels.rightLabel,
|
|
2887
2969
|
// FROM KB - not hardcoded 'After'
|
|
2888
|
-
content: this.truncateText(rightColumn, this.config.rules.wordsPerSlide.max)
|
|
2970
|
+
content: this.truncateText(rightColumn || rightFallback, this.config.rules.wordsPerSlide.max)
|
|
2889
2971
|
}
|
|
2890
2972
|
]
|
|
2891
2973
|
},
|
|
@@ -3156,10 +3238,21 @@ var SlideFactory = class {
|
|
|
3156
3238
|
* Used for section headers with strong conclusions.
|
|
3157
3239
|
*/
|
|
3158
3240
|
createTitleImpactSlide(index, section) {
|
|
3241
|
+
const title = this.cleanText(section.header);
|
|
3159
3242
|
const supportingText = section.content || section.bullets.slice(0, 2).join(". ");
|
|
3160
3243
|
const truncatedSupport = this.truncateText(supportingText, this.config.defaults.context.maxWords);
|
|
3244
|
+
const normalizedTitle = title.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3245
|
+
const normalizedBody = truncatedSupport.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3246
|
+
const bodyIsDuplicate = normalizedBody === normalizedTitle || normalizedTitle.includes(normalizedBody) || normalizedBody.includes(normalizedTitle) || truncatedSupport.length < 10;
|
|
3247
|
+
if (bodyIsDuplicate) {
|
|
3248
|
+
if (section.bullets.length >= 2 && this.config.mode === "business") {
|
|
3249
|
+
return this.createBulletSlide(index, section);
|
|
3250
|
+
}
|
|
3251
|
+
logger.warn(`Skipping title-impact slide "${title}" - no distinct body content`);
|
|
3252
|
+
return null;
|
|
3253
|
+
}
|
|
3161
3254
|
const data = {
|
|
3162
|
-
title
|
|
3255
|
+
title
|
|
3163
3256
|
};
|
|
3164
3257
|
if (truncatedSupport) {
|
|
3165
3258
|
data.body = truncatedSupport;
|
|
@@ -7675,13 +7768,63 @@ var PowerPointGenerator = class {
|
|
|
7675
7768
|
|
|
7676
7769
|
// src/generators/PDFGenerator.ts
|
|
7677
7770
|
var import_puppeteer = __toESM(require("puppeteer"));
|
|
7771
|
+
var cachedBrowser = null;
|
|
7772
|
+
var browserIdleTimeout = null;
|
|
7773
|
+
var BROWSER_IDLE_MS = 3e4;
|
|
7678
7774
|
var PDFGenerator = class {
|
|
7679
7775
|
defaultOptions = {
|
|
7680
7776
|
orientation: "landscape",
|
|
7681
7777
|
printBackground: true,
|
|
7682
7778
|
format: "Slide",
|
|
7683
|
-
scale: 1
|
|
7779
|
+
scale: 1,
|
|
7780
|
+
reuseBrowser: true
|
|
7684
7781
|
};
|
|
7782
|
+
/**
|
|
7783
|
+
* Get or create a browser instance
|
|
7784
|
+
*/
|
|
7785
|
+
async getBrowser(reuse) {
|
|
7786
|
+
if (browserIdleTimeout) {
|
|
7787
|
+
clearTimeout(browserIdleTimeout);
|
|
7788
|
+
browserIdleTimeout = null;
|
|
7789
|
+
}
|
|
7790
|
+
if (reuse && cachedBrowser && cachedBrowser.connected) {
|
|
7791
|
+
logger.progress("Using cached browser instance");
|
|
7792
|
+
return cachedBrowser;
|
|
7793
|
+
}
|
|
7794
|
+
if (cachedBrowser && !cachedBrowser.connected) {
|
|
7795
|
+
cachedBrowser = null;
|
|
7796
|
+
}
|
|
7797
|
+
logger.progress("Launching new browser instance...");
|
|
7798
|
+
const browser = await import_puppeteer.default.launch({
|
|
7799
|
+
headless: true,
|
|
7800
|
+
args: [
|
|
7801
|
+
"--no-sandbox",
|
|
7802
|
+
"--disable-setuid-sandbox",
|
|
7803
|
+
"--disable-dev-shm-usage",
|
|
7804
|
+
"--disable-gpu"
|
|
7805
|
+
]
|
|
7806
|
+
});
|
|
7807
|
+
if (reuse) {
|
|
7808
|
+
cachedBrowser = browser;
|
|
7809
|
+
}
|
|
7810
|
+
return browser;
|
|
7811
|
+
}
|
|
7812
|
+
/**
|
|
7813
|
+
* Schedule browser cleanup after idle period
|
|
7814
|
+
*/
|
|
7815
|
+
scheduleBrowserCleanup() {
|
|
7816
|
+
if (browserIdleTimeout) {
|
|
7817
|
+
clearTimeout(browserIdleTimeout);
|
|
7818
|
+
}
|
|
7819
|
+
browserIdleTimeout = setTimeout(async () => {
|
|
7820
|
+
if (cachedBrowser) {
|
|
7821
|
+
logger.progress("Closing idle browser instance");
|
|
7822
|
+
await cachedBrowser.close().catch(() => {
|
|
7823
|
+
});
|
|
7824
|
+
cachedBrowser = null;
|
|
7825
|
+
}
|
|
7826
|
+
}, BROWSER_IDLE_MS);
|
|
7827
|
+
}
|
|
7685
7828
|
/**
|
|
7686
7829
|
* Generate PDF from HTML presentation
|
|
7687
7830
|
* @param html - The HTML content of the presentation
|
|
@@ -7690,18 +7833,14 @@ var PDFGenerator = class {
|
|
|
7690
7833
|
*/
|
|
7691
7834
|
async generate(html, options = {}) {
|
|
7692
7835
|
const opts = { ...this.defaultOptions, ...options };
|
|
7836
|
+
const reuseBrowser = opts.reuseBrowser ?? true;
|
|
7693
7837
|
logger.progress("\u{1F4C4} Generating PDF...");
|
|
7694
|
-
let browser;
|
|
7838
|
+
let browser = null;
|
|
7839
|
+
let shouldCloseBrowser = !reuseBrowser;
|
|
7840
|
+
let page = null;
|
|
7695
7841
|
try {
|
|
7696
|
-
browser = await
|
|
7697
|
-
|
|
7698
|
-
args: [
|
|
7699
|
-
"--no-sandbox",
|
|
7700
|
-
"--disable-setuid-sandbox",
|
|
7701
|
-
"--disable-dev-shm-usage"
|
|
7702
|
-
]
|
|
7703
|
-
});
|
|
7704
|
-
const page = await browser.newPage();
|
|
7842
|
+
browser = await this.getBrowser(reuseBrowser);
|
|
7843
|
+
page = await browser.newPage();
|
|
7705
7844
|
const printHtml = this.preparePrintHtml(html);
|
|
7706
7845
|
await page.setViewport({
|
|
7707
7846
|
width: 1920,
|
|
@@ -7737,13 +7876,38 @@ var PDFGenerator = class {
|
|
|
7737
7876
|
} catch (error) {
|
|
7738
7877
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7739
7878
|
logger.error(`PDF generation failed: ${errorMessage}`);
|
|
7879
|
+
shouldCloseBrowser = true;
|
|
7740
7880
|
throw new Error(`PDF generation failed: ${errorMessage}`);
|
|
7741
7881
|
} finally {
|
|
7742
|
-
if (
|
|
7743
|
-
await
|
|
7882
|
+
if (page) {
|
|
7883
|
+
await page.close().catch(() => {
|
|
7884
|
+
});
|
|
7885
|
+
}
|
|
7886
|
+
if (shouldCloseBrowser && browser) {
|
|
7887
|
+
await browser.close().catch(() => {
|
|
7888
|
+
});
|
|
7889
|
+
if (browser === cachedBrowser) {
|
|
7890
|
+
cachedBrowser = null;
|
|
7891
|
+
}
|
|
7892
|
+
} else if (reuseBrowser) {
|
|
7893
|
+
this.scheduleBrowserCleanup();
|
|
7744
7894
|
}
|
|
7745
7895
|
}
|
|
7746
7896
|
}
|
|
7897
|
+
/**
|
|
7898
|
+
* Manually close the cached browser (call before process exit)
|
|
7899
|
+
*/
|
|
7900
|
+
static async closeBrowser() {
|
|
7901
|
+
if (browserIdleTimeout) {
|
|
7902
|
+
clearTimeout(browserIdleTimeout);
|
|
7903
|
+
browserIdleTimeout = null;
|
|
7904
|
+
}
|
|
7905
|
+
if (cachedBrowser) {
|
|
7906
|
+
await cachedBrowser.close().catch(() => {
|
|
7907
|
+
});
|
|
7908
|
+
cachedBrowser = null;
|
|
7909
|
+
}
|
|
7910
|
+
}
|
|
7747
7911
|
/**
|
|
7748
7912
|
* Prepare HTML for print/PDF output
|
|
7749
7913
|
* Modifies the HTML to work better as a printed document
|
|
@@ -7757,12 +7921,24 @@ var PDFGenerator = class {
|
|
|
7757
7921
|
margin: 0;
|
|
7758
7922
|
}
|
|
7759
7923
|
|
|
7924
|
+
/* Font rendering optimization for print */
|
|
7925
|
+
* {
|
|
7926
|
+
-webkit-font-smoothing: antialiased;
|
|
7927
|
+
-moz-osx-font-smoothing: grayscale;
|
|
7928
|
+
text-rendering: optimizeLegibility;
|
|
7929
|
+
font-feature-settings: "liga" 1, "kern" 1;
|
|
7930
|
+
}
|
|
7931
|
+
|
|
7760
7932
|
@media print {
|
|
7761
7933
|
html, body {
|
|
7762
7934
|
margin: 0;
|
|
7763
7935
|
padding: 0;
|
|
7764
7936
|
width: 1920px;
|
|
7765
7937
|
height: 1080px;
|
|
7938
|
+
/* Print color optimization */
|
|
7939
|
+
color-adjust: exact;
|
|
7940
|
+
-webkit-print-color-adjust: exact;
|
|
7941
|
+
print-color-adjust: exact;
|
|
7766
7942
|
}
|
|
7767
7943
|
|
|
7768
7944
|
.reveal .slides {
|
|
@@ -7779,7 +7955,8 @@ var PDFGenerator = class {
|
|
|
7779
7955
|
width: 1920px !important;
|
|
7780
7956
|
height: 1080px !important;
|
|
7781
7957
|
margin: 0 !important;
|
|
7782
|
-
|
|
7958
|
+
/* Professional safe margins: 80px (4.2%) */
|
|
7959
|
+
padding: 80px !important;
|
|
7783
7960
|
box-sizing: border-box;
|
|
7784
7961
|
position: relative !important;
|
|
7785
7962
|
top: auto !important;
|
|
@@ -7803,7 +7980,7 @@ var PDFGenerator = class {
|
|
|
7803
7980
|
}
|
|
7804
7981
|
|
|
7805
7982
|
/* Disable animations for print */
|
|
7806
|
-
|
|
7983
|
+
*, *::before, *::after {
|
|
7807
7984
|
animation: none !important;
|
|
7808
7985
|
transition: none !important;
|
|
7809
7986
|
}
|
|
@@ -7819,6 +7996,30 @@ var PDFGenerator = class {
|
|
|
7819
7996
|
.reveal .navigate-down {
|
|
7820
7997
|
display: none !important;
|
|
7821
7998
|
}
|
|
7999
|
+
|
|
8000
|
+
/* Typography refinements for print */
|
|
8001
|
+
h1, h2, h3, h4 {
|
|
8002
|
+
orphans: 3;
|
|
8003
|
+
widows: 3;
|
|
8004
|
+
page-break-after: avoid;
|
|
8005
|
+
}
|
|
8006
|
+
|
|
8007
|
+
p, li {
|
|
8008
|
+
orphans: 2;
|
|
8009
|
+
widows: 2;
|
|
8010
|
+
}
|
|
8011
|
+
|
|
8012
|
+
/* Ensure links are readable in print */
|
|
8013
|
+
a {
|
|
8014
|
+
text-decoration: none;
|
|
8015
|
+
}
|
|
8016
|
+
|
|
8017
|
+
/* Prevent image overflow */
|
|
8018
|
+
img {
|
|
8019
|
+
max-width: 100%;
|
|
8020
|
+
height: auto;
|
|
8021
|
+
page-break-inside: avoid;
|
|
8022
|
+
}
|
|
7822
8023
|
}
|
|
7823
8024
|
|
|
7824
8025
|
/* Force print mode in Puppeteer */
|
|
@@ -7826,6 +8027,11 @@ var PDFGenerator = class {
|
|
|
7826
8027
|
page-break-after: always;
|
|
7827
8028
|
page-break-inside: avoid;
|
|
7828
8029
|
}
|
|
8030
|
+
|
|
8031
|
+
/* High contrast mode for business presentations */
|
|
8032
|
+
.reveal section {
|
|
8033
|
+
color-scheme: light;
|
|
8034
|
+
}
|
|
7829
8035
|
</style>
|
|
7830
8036
|
`;
|
|
7831
8037
|
if (html.includes("</head>")) {
|
|
@@ -8419,6 +8625,7 @@ var index_default = {
|
|
|
8419
8625
|
CompositeImageProvider,
|
|
8420
8626
|
ContentAnalyzer,
|
|
8421
8627
|
ContentPatternClassifier,
|
|
8628
|
+
KnowledgeBaseError,
|
|
8422
8629
|
KnowledgeGateway,
|
|
8423
8630
|
LocalImageProvider,
|
|
8424
8631
|
MermaidProvider,
|
package/dist/index.mjs
CHANGED
|
@@ -1,30 +1,98 @@
|
|
|
1
1
|
// src/types/index.ts
|
|
2
|
-
var ValidationError = class extends Error {
|
|
2
|
+
var ValidationError = class _ValidationError extends Error {
|
|
3
3
|
constructor(errors, message = "Validation failed") {
|
|
4
|
-
|
|
4
|
+
const errorList = errors.map((e) => ` \u2022 ${e}`).join("\n");
|
|
5
|
+
const suggestions = _ValidationError.getSuggestions(errors);
|
|
6
|
+
const suggestionList = suggestions.length > 0 ? "\n\nHow to fix:\n" + suggestions.map((s) => ` \u2192 ${s}`).join("\n") : "";
|
|
7
|
+
super(`${message}
|
|
8
|
+
|
|
9
|
+
Issues found:
|
|
10
|
+
${errorList}${suggestionList}`);
|
|
5
11
|
this.errors = errors;
|
|
6
12
|
this.name = "ValidationError";
|
|
13
|
+
this.suggestions = suggestions;
|
|
14
|
+
}
|
|
15
|
+
suggestions;
|
|
16
|
+
static getSuggestions(errors) {
|
|
17
|
+
const suggestions = [];
|
|
18
|
+
for (const error of errors) {
|
|
19
|
+
if (error.includes("Content is required")) {
|
|
20
|
+
suggestions.push("Provide markdown content with at least one ## section header");
|
|
21
|
+
}
|
|
22
|
+
if (error.includes("Mode must be")) {
|
|
23
|
+
suggestions.push('Use mode: "keynote" for TED-style or mode: "business" for corporate');
|
|
24
|
+
}
|
|
25
|
+
if (error.includes("Title is required")) {
|
|
26
|
+
suggestions.push('Add a title: "Your Presentation Title" to your config');
|
|
27
|
+
}
|
|
28
|
+
if (error.includes("output format")) {
|
|
29
|
+
suggestions.push('Specify format: ["html"] or format: ["html", "pdf"]');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return [...new Set(suggestions)];
|
|
7
33
|
}
|
|
8
34
|
};
|
|
9
|
-
var QAFailureError = class extends Error {
|
|
10
|
-
constructor(score, threshold, qaResults, message
|
|
11
|
-
|
|
35
|
+
var QAFailureError = class _QAFailureError extends Error {
|
|
36
|
+
constructor(score, threshold, qaResults, message) {
|
|
37
|
+
const topIssues = _QAFailureError.getTopIssues(qaResults);
|
|
38
|
+
const issueList = topIssues.map((i) => ` \u2022 ${i}`).join("\n");
|
|
39
|
+
const helpText = `
|
|
40
|
+
|
|
41
|
+
Score: ${score}/100 (threshold: ${threshold})
|
|
42
|
+
|
|
43
|
+
Top issues to fix:
|
|
44
|
+
${issueList}
|
|
45
|
+
|
|
46
|
+
How to improve:
|
|
47
|
+
\u2192 Ensure each slide has meaningful content (not just a title)
|
|
48
|
+
\u2192 Keep text concise - aim for <40 words per slide
|
|
49
|
+
\u2192 Use timeline/process slides for step-by-step content
|
|
50
|
+
\u2192 Add visuals for data-heavy slides`;
|
|
51
|
+
super(message || `QA score ${score} below threshold ${threshold}${helpText}`);
|
|
12
52
|
this.score = score;
|
|
13
53
|
this.threshold = threshold;
|
|
14
54
|
this.qaResults = qaResults;
|
|
15
55
|
this.name = "QAFailureError";
|
|
56
|
+
this.topIssues = topIssues;
|
|
16
57
|
}
|
|
58
|
+
topIssues;
|
|
17
59
|
getIssues() {
|
|
18
60
|
return this.qaResults.issues.map((issue) => issue.message);
|
|
19
61
|
}
|
|
62
|
+
static getTopIssues(qaResults) {
|
|
63
|
+
const issues = [];
|
|
64
|
+
if (qaResults.slideScores) {
|
|
65
|
+
for (const slide of qaResults.slideScores) {
|
|
66
|
+
if (slide.criticalIssues) {
|
|
67
|
+
issues.push(...slide.criticalIssues);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (qaResults.issues) {
|
|
72
|
+
issues.push(...qaResults.issues.map((i) => i.message));
|
|
73
|
+
}
|
|
74
|
+
return [...new Set(issues)].slice(0, 5);
|
|
75
|
+
}
|
|
20
76
|
};
|
|
21
77
|
var TemplateNotFoundError = class extends Error {
|
|
22
|
-
constructor(templatePath, message
|
|
23
|
-
super(message
|
|
78
|
+
constructor(templatePath, message) {
|
|
79
|
+
super(message || `Template not found: ${templatePath}
|
|
80
|
+
|
|
81
|
+
Available templates: title, content, timeline, process, metrics, quote, cta, thank-you`);
|
|
24
82
|
this.templatePath = templatePath;
|
|
25
83
|
this.name = "TemplateNotFoundError";
|
|
26
84
|
}
|
|
27
85
|
};
|
|
86
|
+
var KnowledgeBaseError = class extends Error {
|
|
87
|
+
constructor(field, context, message) {
|
|
88
|
+
super(message || `Knowledge base configuration error: Missing "${field}" in ${context}
|
|
89
|
+
|
|
90
|
+
Check assets/presentation-knowledge.yaml for the correct structure.`);
|
|
91
|
+
this.field = field;
|
|
92
|
+
this.context = context;
|
|
93
|
+
this.name = "KnowledgeBaseError";
|
|
94
|
+
}
|
|
95
|
+
};
|
|
28
96
|
|
|
29
97
|
// src/core/PresentationEngine.ts
|
|
30
98
|
import * as fs2 from "fs";
|
|
@@ -33,8 +101,7 @@ import * as os from "os";
|
|
|
33
101
|
|
|
34
102
|
// src/kb/KnowledgeGateway.ts
|
|
35
103
|
import { readFileSync } from "fs";
|
|
36
|
-
import { join
|
|
37
|
-
import { fileURLToPath } from "url";
|
|
104
|
+
import { join } from "path";
|
|
38
105
|
import * as yaml from "yaml";
|
|
39
106
|
|
|
40
107
|
// src/utils/Logger.ts
|
|
@@ -106,9 +173,6 @@ var logger = new Logger({
|
|
|
106
173
|
|
|
107
174
|
// src/kb/KnowledgeGateway.ts
|
|
108
175
|
function getModuleDir() {
|
|
109
|
-
if (typeof import.meta !== "undefined" && import.meta.url) {
|
|
110
|
-
return dirname(fileURLToPath(import.meta.url));
|
|
111
|
-
}
|
|
112
176
|
if (typeof __dirname !== "undefined") {
|
|
113
177
|
return __dirname;
|
|
114
178
|
}
|
|
@@ -2798,25 +2862,43 @@ var SlideFactory = class {
|
|
|
2798
2862
|
createComparisonSlide(index, section) {
|
|
2799
2863
|
const comparison = this.classifier.extractComparison(section);
|
|
2800
2864
|
const labels = this.config.defaults.comparison;
|
|
2865
|
+
const title = this.createTitle(section.header, section);
|
|
2866
|
+
const normalizedTitle = title.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2801
2867
|
const leftFallback = labels.optionLabels[0] ?? "Option A";
|
|
2802
2868
|
const rightFallback = labels.optionLabels[1] ?? "Option B";
|
|
2803
|
-
|
|
2804
|
-
|
|
2869
|
+
let leftColumn = comparison?.left || section.bullets[0] || "";
|
|
2870
|
+
let rightColumn = comparison?.right || section.bullets[1] || "";
|
|
2871
|
+
const normalizedLeft = leftColumn.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2872
|
+
const normalizedRight = rightColumn.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2873
|
+
const leftIsDuplicate = normalizedLeft === normalizedTitle || normalizedTitle.includes(normalizedLeft);
|
|
2874
|
+
const rightIsDuplicate = normalizedRight === normalizedTitle || normalizedTitle.includes(normalizedRight);
|
|
2875
|
+
if (leftIsDuplicate || rightIsDuplicate || !leftColumn && !rightColumn) {
|
|
2876
|
+
if (section.bullets.length >= 2) {
|
|
2877
|
+
logger.warn(`Comparison slide "${title}" has duplicate content, using bullet points instead`);
|
|
2878
|
+
return this.createBulletSlide(index, section);
|
|
2879
|
+
}
|
|
2880
|
+
if (leftIsDuplicate && rightIsDuplicate) {
|
|
2881
|
+
logger.warn(`Skipping comparison slide "${title}" - content duplicates title`);
|
|
2882
|
+
return null;
|
|
2883
|
+
}
|
|
2884
|
+
if (leftIsDuplicate) leftColumn = leftFallback;
|
|
2885
|
+
if (rightIsDuplicate) rightColumn = rightFallback;
|
|
2886
|
+
}
|
|
2805
2887
|
return {
|
|
2806
2888
|
index,
|
|
2807
2889
|
type: "comparison",
|
|
2808
2890
|
data: {
|
|
2809
|
-
title
|
|
2891
|
+
title,
|
|
2810
2892
|
columns: [
|
|
2811
2893
|
{
|
|
2812
2894
|
title: labels.leftLabel,
|
|
2813
2895
|
// FROM KB - not hardcoded 'Before'
|
|
2814
|
-
content: this.truncateText(leftColumn, this.config.rules.wordsPerSlide.max)
|
|
2896
|
+
content: this.truncateText(leftColumn || leftFallback, this.config.rules.wordsPerSlide.max)
|
|
2815
2897
|
},
|
|
2816
2898
|
{
|
|
2817
2899
|
title: labels.rightLabel,
|
|
2818
2900
|
// FROM KB - not hardcoded 'After'
|
|
2819
|
-
content: this.truncateText(rightColumn, this.config.rules.wordsPerSlide.max)
|
|
2901
|
+
content: this.truncateText(rightColumn || rightFallback, this.config.rules.wordsPerSlide.max)
|
|
2820
2902
|
}
|
|
2821
2903
|
]
|
|
2822
2904
|
},
|
|
@@ -3087,10 +3169,21 @@ var SlideFactory = class {
|
|
|
3087
3169
|
* Used for section headers with strong conclusions.
|
|
3088
3170
|
*/
|
|
3089
3171
|
createTitleImpactSlide(index, section) {
|
|
3172
|
+
const title = this.cleanText(section.header);
|
|
3090
3173
|
const supportingText = section.content || section.bullets.slice(0, 2).join(". ");
|
|
3091
3174
|
const truncatedSupport = this.truncateText(supportingText, this.config.defaults.context.maxWords);
|
|
3175
|
+
const normalizedTitle = title.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3176
|
+
const normalizedBody = truncatedSupport.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
3177
|
+
const bodyIsDuplicate = normalizedBody === normalizedTitle || normalizedTitle.includes(normalizedBody) || normalizedBody.includes(normalizedTitle) || truncatedSupport.length < 10;
|
|
3178
|
+
if (bodyIsDuplicate) {
|
|
3179
|
+
if (section.bullets.length >= 2 && this.config.mode === "business") {
|
|
3180
|
+
return this.createBulletSlide(index, section);
|
|
3181
|
+
}
|
|
3182
|
+
logger.warn(`Skipping title-impact slide "${title}" - no distinct body content`);
|
|
3183
|
+
return null;
|
|
3184
|
+
}
|
|
3092
3185
|
const data = {
|
|
3093
|
-
title
|
|
3186
|
+
title
|
|
3094
3187
|
};
|
|
3095
3188
|
if (truncatedSupport) {
|
|
3096
3189
|
data.body = truncatedSupport;
|
|
@@ -7606,13 +7699,63 @@ var PowerPointGenerator = class {
|
|
|
7606
7699
|
|
|
7607
7700
|
// src/generators/PDFGenerator.ts
|
|
7608
7701
|
import puppeteer from "puppeteer";
|
|
7702
|
+
var cachedBrowser = null;
|
|
7703
|
+
var browserIdleTimeout = null;
|
|
7704
|
+
var BROWSER_IDLE_MS = 3e4;
|
|
7609
7705
|
var PDFGenerator = class {
|
|
7610
7706
|
defaultOptions = {
|
|
7611
7707
|
orientation: "landscape",
|
|
7612
7708
|
printBackground: true,
|
|
7613
7709
|
format: "Slide",
|
|
7614
|
-
scale: 1
|
|
7710
|
+
scale: 1,
|
|
7711
|
+
reuseBrowser: true
|
|
7615
7712
|
};
|
|
7713
|
+
/**
|
|
7714
|
+
* Get or create a browser instance
|
|
7715
|
+
*/
|
|
7716
|
+
async getBrowser(reuse) {
|
|
7717
|
+
if (browserIdleTimeout) {
|
|
7718
|
+
clearTimeout(browserIdleTimeout);
|
|
7719
|
+
browserIdleTimeout = null;
|
|
7720
|
+
}
|
|
7721
|
+
if (reuse && cachedBrowser && cachedBrowser.connected) {
|
|
7722
|
+
logger.progress("Using cached browser instance");
|
|
7723
|
+
return cachedBrowser;
|
|
7724
|
+
}
|
|
7725
|
+
if (cachedBrowser && !cachedBrowser.connected) {
|
|
7726
|
+
cachedBrowser = null;
|
|
7727
|
+
}
|
|
7728
|
+
logger.progress("Launching new browser instance...");
|
|
7729
|
+
const browser = await puppeteer.launch({
|
|
7730
|
+
headless: true,
|
|
7731
|
+
args: [
|
|
7732
|
+
"--no-sandbox",
|
|
7733
|
+
"--disable-setuid-sandbox",
|
|
7734
|
+
"--disable-dev-shm-usage",
|
|
7735
|
+
"--disable-gpu"
|
|
7736
|
+
]
|
|
7737
|
+
});
|
|
7738
|
+
if (reuse) {
|
|
7739
|
+
cachedBrowser = browser;
|
|
7740
|
+
}
|
|
7741
|
+
return browser;
|
|
7742
|
+
}
|
|
7743
|
+
/**
|
|
7744
|
+
* Schedule browser cleanup after idle period
|
|
7745
|
+
*/
|
|
7746
|
+
scheduleBrowserCleanup() {
|
|
7747
|
+
if (browserIdleTimeout) {
|
|
7748
|
+
clearTimeout(browserIdleTimeout);
|
|
7749
|
+
}
|
|
7750
|
+
browserIdleTimeout = setTimeout(async () => {
|
|
7751
|
+
if (cachedBrowser) {
|
|
7752
|
+
logger.progress("Closing idle browser instance");
|
|
7753
|
+
await cachedBrowser.close().catch(() => {
|
|
7754
|
+
});
|
|
7755
|
+
cachedBrowser = null;
|
|
7756
|
+
}
|
|
7757
|
+
}, BROWSER_IDLE_MS);
|
|
7758
|
+
}
|
|
7616
7759
|
/**
|
|
7617
7760
|
* Generate PDF from HTML presentation
|
|
7618
7761
|
* @param html - The HTML content of the presentation
|
|
@@ -7621,18 +7764,14 @@ var PDFGenerator = class {
|
|
|
7621
7764
|
*/
|
|
7622
7765
|
async generate(html, options = {}) {
|
|
7623
7766
|
const opts = { ...this.defaultOptions, ...options };
|
|
7767
|
+
const reuseBrowser = opts.reuseBrowser ?? true;
|
|
7624
7768
|
logger.progress("\u{1F4C4} Generating PDF...");
|
|
7625
|
-
let browser;
|
|
7769
|
+
let browser = null;
|
|
7770
|
+
let shouldCloseBrowser = !reuseBrowser;
|
|
7771
|
+
let page = null;
|
|
7626
7772
|
try {
|
|
7627
|
-
browser = await
|
|
7628
|
-
|
|
7629
|
-
args: [
|
|
7630
|
-
"--no-sandbox",
|
|
7631
|
-
"--disable-setuid-sandbox",
|
|
7632
|
-
"--disable-dev-shm-usage"
|
|
7633
|
-
]
|
|
7634
|
-
});
|
|
7635
|
-
const page = await browser.newPage();
|
|
7773
|
+
browser = await this.getBrowser(reuseBrowser);
|
|
7774
|
+
page = await browser.newPage();
|
|
7636
7775
|
const printHtml = this.preparePrintHtml(html);
|
|
7637
7776
|
await page.setViewport({
|
|
7638
7777
|
width: 1920,
|
|
@@ -7668,13 +7807,38 @@ var PDFGenerator = class {
|
|
|
7668
7807
|
} catch (error) {
|
|
7669
7808
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7670
7809
|
logger.error(`PDF generation failed: ${errorMessage}`);
|
|
7810
|
+
shouldCloseBrowser = true;
|
|
7671
7811
|
throw new Error(`PDF generation failed: ${errorMessage}`);
|
|
7672
7812
|
} finally {
|
|
7673
|
-
if (
|
|
7674
|
-
await
|
|
7813
|
+
if (page) {
|
|
7814
|
+
await page.close().catch(() => {
|
|
7815
|
+
});
|
|
7816
|
+
}
|
|
7817
|
+
if (shouldCloseBrowser && browser) {
|
|
7818
|
+
await browser.close().catch(() => {
|
|
7819
|
+
});
|
|
7820
|
+
if (browser === cachedBrowser) {
|
|
7821
|
+
cachedBrowser = null;
|
|
7822
|
+
}
|
|
7823
|
+
} else if (reuseBrowser) {
|
|
7824
|
+
this.scheduleBrowserCleanup();
|
|
7675
7825
|
}
|
|
7676
7826
|
}
|
|
7677
7827
|
}
|
|
7828
|
+
/**
|
|
7829
|
+
* Manually close the cached browser (call before process exit)
|
|
7830
|
+
*/
|
|
7831
|
+
static async closeBrowser() {
|
|
7832
|
+
if (browserIdleTimeout) {
|
|
7833
|
+
clearTimeout(browserIdleTimeout);
|
|
7834
|
+
browserIdleTimeout = null;
|
|
7835
|
+
}
|
|
7836
|
+
if (cachedBrowser) {
|
|
7837
|
+
await cachedBrowser.close().catch(() => {
|
|
7838
|
+
});
|
|
7839
|
+
cachedBrowser = null;
|
|
7840
|
+
}
|
|
7841
|
+
}
|
|
7678
7842
|
/**
|
|
7679
7843
|
* Prepare HTML for print/PDF output
|
|
7680
7844
|
* Modifies the HTML to work better as a printed document
|
|
@@ -7688,12 +7852,24 @@ var PDFGenerator = class {
|
|
|
7688
7852
|
margin: 0;
|
|
7689
7853
|
}
|
|
7690
7854
|
|
|
7855
|
+
/* Font rendering optimization for print */
|
|
7856
|
+
* {
|
|
7857
|
+
-webkit-font-smoothing: antialiased;
|
|
7858
|
+
-moz-osx-font-smoothing: grayscale;
|
|
7859
|
+
text-rendering: optimizeLegibility;
|
|
7860
|
+
font-feature-settings: "liga" 1, "kern" 1;
|
|
7861
|
+
}
|
|
7862
|
+
|
|
7691
7863
|
@media print {
|
|
7692
7864
|
html, body {
|
|
7693
7865
|
margin: 0;
|
|
7694
7866
|
padding: 0;
|
|
7695
7867
|
width: 1920px;
|
|
7696
7868
|
height: 1080px;
|
|
7869
|
+
/* Print color optimization */
|
|
7870
|
+
color-adjust: exact;
|
|
7871
|
+
-webkit-print-color-adjust: exact;
|
|
7872
|
+
print-color-adjust: exact;
|
|
7697
7873
|
}
|
|
7698
7874
|
|
|
7699
7875
|
.reveal .slides {
|
|
@@ -7710,7 +7886,8 @@ var PDFGenerator = class {
|
|
|
7710
7886
|
width: 1920px !important;
|
|
7711
7887
|
height: 1080px !important;
|
|
7712
7888
|
margin: 0 !important;
|
|
7713
|
-
|
|
7889
|
+
/* Professional safe margins: 80px (4.2%) */
|
|
7890
|
+
padding: 80px !important;
|
|
7714
7891
|
box-sizing: border-box;
|
|
7715
7892
|
position: relative !important;
|
|
7716
7893
|
top: auto !important;
|
|
@@ -7734,7 +7911,7 @@ var PDFGenerator = class {
|
|
|
7734
7911
|
}
|
|
7735
7912
|
|
|
7736
7913
|
/* Disable animations for print */
|
|
7737
|
-
|
|
7914
|
+
*, *::before, *::after {
|
|
7738
7915
|
animation: none !important;
|
|
7739
7916
|
transition: none !important;
|
|
7740
7917
|
}
|
|
@@ -7750,6 +7927,30 @@ var PDFGenerator = class {
|
|
|
7750
7927
|
.reveal .navigate-down {
|
|
7751
7928
|
display: none !important;
|
|
7752
7929
|
}
|
|
7930
|
+
|
|
7931
|
+
/* Typography refinements for print */
|
|
7932
|
+
h1, h2, h3, h4 {
|
|
7933
|
+
orphans: 3;
|
|
7934
|
+
widows: 3;
|
|
7935
|
+
page-break-after: avoid;
|
|
7936
|
+
}
|
|
7937
|
+
|
|
7938
|
+
p, li {
|
|
7939
|
+
orphans: 2;
|
|
7940
|
+
widows: 2;
|
|
7941
|
+
}
|
|
7942
|
+
|
|
7943
|
+
/* Ensure links are readable in print */
|
|
7944
|
+
a {
|
|
7945
|
+
text-decoration: none;
|
|
7946
|
+
}
|
|
7947
|
+
|
|
7948
|
+
/* Prevent image overflow */
|
|
7949
|
+
img {
|
|
7950
|
+
max-width: 100%;
|
|
7951
|
+
height: auto;
|
|
7952
|
+
page-break-inside: avoid;
|
|
7953
|
+
}
|
|
7753
7954
|
}
|
|
7754
7955
|
|
|
7755
7956
|
/* Force print mode in Puppeteer */
|
|
@@ -7757,6 +7958,11 @@ var PDFGenerator = class {
|
|
|
7757
7958
|
page-break-after: always;
|
|
7758
7959
|
page-break-inside: avoid;
|
|
7759
7960
|
}
|
|
7961
|
+
|
|
7962
|
+
/* High contrast mode for business presentations */
|
|
7963
|
+
.reveal section {
|
|
7964
|
+
color-scheme: light;
|
|
7965
|
+
}
|
|
7760
7966
|
</style>
|
|
7761
7967
|
`;
|
|
7762
7968
|
if (html.includes("</head>")) {
|
|
@@ -8349,6 +8555,7 @@ export {
|
|
|
8349
8555
|
CompositeImageProvider,
|
|
8350
8556
|
ContentAnalyzer,
|
|
8351
8557
|
ContentPatternClassifier,
|
|
8558
|
+
KnowledgeBaseError,
|
|
8352
8559
|
KnowledgeGateway,
|
|
8353
8560
|
LocalImageProvider,
|
|
8354
8561
|
MermaidProvider,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-presentation-master",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "Generate world-class presentations using expert methodologies from Duarte, Reynolds, Gallo, and Anderson. Enforces rigorous quality standards through real visual validation.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|