claude-presentation-master 6.0.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +77 -13
- package/dist/index.d.ts +77 -13
- package/dist/index.js +286 -81
- package/dist/index.mjs +286 -81
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -254,13 +254,41 @@ interface PresentationMetadata {
|
|
|
254
254
|
/** Themes/frameworks used */
|
|
255
255
|
frameworks: string[];
|
|
256
256
|
}
|
|
257
|
+
interface ContentSection$1 {
|
|
258
|
+
/** Section header text */
|
|
259
|
+
header: string;
|
|
260
|
+
/** Header level (1-6) */
|
|
261
|
+
level: number;
|
|
262
|
+
/** Body content */
|
|
263
|
+
content: string;
|
|
264
|
+
/** Bullet points */
|
|
265
|
+
bullets: string[];
|
|
266
|
+
/** Metrics extracted from section */
|
|
267
|
+
metrics: Array<{
|
|
268
|
+
value: string;
|
|
269
|
+
label: string;
|
|
270
|
+
context?: string;
|
|
271
|
+
}>;
|
|
272
|
+
}
|
|
257
273
|
interface ContentAnalysis$1 {
|
|
274
|
+
/** Detected presentation type */
|
|
275
|
+
detectedType: PresentationType$1;
|
|
276
|
+
/** Main title */
|
|
277
|
+
title: string;
|
|
278
|
+
/** Content sections */
|
|
279
|
+
sections: ContentSection$1[];
|
|
258
280
|
/** SCQA structure extracted */
|
|
259
281
|
scqa: SCQAStructure;
|
|
260
282
|
/** Sparkline narrative arc */
|
|
261
283
|
sparkline: SparklineStructure;
|
|
262
284
|
/** Key messages identified */
|
|
263
285
|
keyMessages: string[];
|
|
286
|
+
/** Data points (metrics, percentages, etc.) */
|
|
287
|
+
dataPoints: Array<{
|
|
288
|
+
value: string;
|
|
289
|
+
label: string;
|
|
290
|
+
context?: string;
|
|
291
|
+
}>;
|
|
264
292
|
/** Generated action titles */
|
|
265
293
|
titles: string[];
|
|
266
294
|
/** STAR moments identified */
|
|
@@ -611,7 +639,15 @@ declare class ContentAnalyzer {
|
|
|
611
639
|
private extractSCQA;
|
|
612
640
|
/**
|
|
613
641
|
* Extract STAR moments (Something They'll Always Remember)
|
|
614
|
-
* Per Nancy Duarte
|
|
642
|
+
* Per Nancy Duarte: These should be memorable, COMPLETE thoughts with impact
|
|
643
|
+
*
|
|
644
|
+
* CRITICAL REQUIREMENTS:
|
|
645
|
+
* - Must be complete sentences with subject + verb structure
|
|
646
|
+
* - Must have 5+ words minimum (no fragments!)
|
|
647
|
+
* - Must be 30+ characters
|
|
648
|
+
* - Must contain a verb
|
|
649
|
+
* - NOT fragments like "significant growth" or "cloud-first strategy"
|
|
650
|
+
* - NOT headers or topic labels
|
|
615
651
|
*/
|
|
616
652
|
private extractStarMoments;
|
|
617
653
|
/**
|
|
@@ -619,28 +655,27 @@ declare class ContentAnalyzer {
|
|
|
619
655
|
*/
|
|
620
656
|
private extractSparkline;
|
|
621
657
|
/**
|
|
622
|
-
* Extract key messages
|
|
658
|
+
* Extract key messages - the actual insights from the content
|
|
659
|
+
* Per Carmine Gallo: Rule of Three - max 3 key messages
|
|
660
|
+
* Per Barbara Minto: Messages should be actionable conclusions, not topics
|
|
623
661
|
*/
|
|
624
662
|
private extractKeyMessages;
|
|
625
663
|
/**
|
|
626
664
|
* Extract data points (metrics with values)
|
|
665
|
+
* IMPROVED: Smarter label extraction that understands markdown tables
|
|
627
666
|
*/
|
|
628
667
|
private extractDataPoints;
|
|
629
668
|
/**
|
|
630
|
-
*
|
|
669
|
+
* Extract a meaningful label from a line containing a metric
|
|
631
670
|
*/
|
|
632
|
-
private
|
|
633
|
-
/**
|
|
634
|
-
* Extract a label from surrounding context
|
|
635
|
-
* Fixes the "Century Interactive |" garbage issue by stripping table syntax
|
|
636
|
-
*/
|
|
637
|
-
private extractLabelFromContext;
|
|
671
|
+
private extractLabelFromLine;
|
|
638
672
|
/**
|
|
639
673
|
* Check if text contains any of the signals
|
|
640
674
|
*/
|
|
641
675
|
private containsSignals;
|
|
642
676
|
/**
|
|
643
677
|
* Extract first meaningful sentence from text
|
|
678
|
+
* CRITICAL: Must strip headers, CTA text, and fragments
|
|
644
679
|
*/
|
|
645
680
|
private extractFirstSentence;
|
|
646
681
|
}
|
|
@@ -664,8 +699,35 @@ declare class SlideFactory {
|
|
|
664
699
|
private isContentUsed;
|
|
665
700
|
/**
|
|
666
701
|
* Create slides from analyzed content.
|
|
702
|
+
*
|
|
703
|
+
* ARCHITECTURE (per KB expert methodologies):
|
|
704
|
+
* 1. Title slide - always first
|
|
705
|
+
* 2. Agenda slide - business mode only with 3+ sections
|
|
706
|
+
* 3. SCQA slides - Situation, Complication (per Minto)
|
|
707
|
+
* 4. Content slides - from sections with bullets/content
|
|
708
|
+
* 5. Metrics slide - if data points exist
|
|
709
|
+
* 6. Solution slide - SCQA answer
|
|
710
|
+
* 7. STAR moments - only if high-quality (per Duarte)
|
|
711
|
+
* 8. CTA slide - if call to action exists
|
|
712
|
+
* 9. Thank you slide - always last
|
|
667
713
|
*/
|
|
668
714
|
createSlides(analysis: ContentAnalysis$1, mode: PresentationMode$1): Promise<Slide[]>;
|
|
715
|
+
/**
|
|
716
|
+
* Create a slide from a section with bullets
|
|
717
|
+
*/
|
|
718
|
+
private createSectionBulletSlide;
|
|
719
|
+
/**
|
|
720
|
+
* Create a slide from a section with body content
|
|
721
|
+
*/
|
|
722
|
+
private createSectionContentSlide;
|
|
723
|
+
/**
|
|
724
|
+
* Extract first sentence from text
|
|
725
|
+
*/
|
|
726
|
+
private extractFirstSentence;
|
|
727
|
+
/**
|
|
728
|
+
* Create a metrics slide from data points
|
|
729
|
+
*/
|
|
730
|
+
private createMetricsSlide;
|
|
669
731
|
/**
|
|
670
732
|
* Create a title slide.
|
|
671
733
|
*/
|
|
@@ -707,11 +769,13 @@ declare class SlideFactory {
|
|
|
707
769
|
*/
|
|
708
770
|
private initializeTemplates;
|
|
709
771
|
/**
|
|
710
|
-
* Clean text by removing all content markers.
|
|
772
|
+
* Clean text by removing all markdown and content markers.
|
|
773
|
+
* CRITICAL: Must strip all formatting to prevent garbage in slides
|
|
711
774
|
*/
|
|
712
775
|
private cleanText;
|
|
713
776
|
/**
|
|
714
|
-
* Truncate text to max length at
|
|
777
|
+
* Truncate text to max length at sentence boundary when possible.
|
|
778
|
+
* CRITICAL: Never cut mid-number (99.5% should not become 99.)
|
|
715
779
|
*/
|
|
716
780
|
private truncate;
|
|
717
781
|
/**
|
|
@@ -723,7 +787,7 @@ declare class SlideFactory {
|
|
|
723
787
|
*/
|
|
724
788
|
private extractBullets;
|
|
725
789
|
/**
|
|
726
|
-
* Remove a statistic from text.
|
|
790
|
+
* Remove a statistic from text and clean thoroughly.
|
|
727
791
|
*/
|
|
728
792
|
private removeStatistic;
|
|
729
793
|
}
|
|
@@ -1359,4 +1423,4 @@ declare const _default: {
|
|
|
1359
1423
|
VERSION: string;
|
|
1360
1424
|
};
|
|
1361
1425
|
|
|
1362
|
-
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 ContentQAResults, type ContrastIssue, type ExpertQAResults, type ExpertValidation, type FontSizeIssue, type GlanceTestResult, type ImageData, type ImageProvider, type ImageRequest, type ImageResult, KnowledgeGateway, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, type SlideTemplate, type SlideType, type SlideVisualScore, type SparklineStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, UnsplashImageProvider, VERSION, ValidationError, type VisualQAResults, createDefaultChartProvider, createDefaultImageProvider, _default as default, generate, getKnowledgeGateway, validate };
|
|
1426
|
+
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 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, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, type SlideTemplate, type SlideType, type SlideVisualScore, type SparklineStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, UnsplashImageProvider, VERSION, ValidationError, type VisualQAResults, createDefaultChartProvider, createDefaultImageProvider, _default as default, generate, getKnowledgeGateway, validate };
|
package/dist/index.d.ts
CHANGED
|
@@ -254,13 +254,41 @@ interface PresentationMetadata {
|
|
|
254
254
|
/** Themes/frameworks used */
|
|
255
255
|
frameworks: string[];
|
|
256
256
|
}
|
|
257
|
+
interface ContentSection$1 {
|
|
258
|
+
/** Section header text */
|
|
259
|
+
header: string;
|
|
260
|
+
/** Header level (1-6) */
|
|
261
|
+
level: number;
|
|
262
|
+
/** Body content */
|
|
263
|
+
content: string;
|
|
264
|
+
/** Bullet points */
|
|
265
|
+
bullets: string[];
|
|
266
|
+
/** Metrics extracted from section */
|
|
267
|
+
metrics: Array<{
|
|
268
|
+
value: string;
|
|
269
|
+
label: string;
|
|
270
|
+
context?: string;
|
|
271
|
+
}>;
|
|
272
|
+
}
|
|
257
273
|
interface ContentAnalysis$1 {
|
|
274
|
+
/** Detected presentation type */
|
|
275
|
+
detectedType: PresentationType$1;
|
|
276
|
+
/** Main title */
|
|
277
|
+
title: string;
|
|
278
|
+
/** Content sections */
|
|
279
|
+
sections: ContentSection$1[];
|
|
258
280
|
/** SCQA structure extracted */
|
|
259
281
|
scqa: SCQAStructure;
|
|
260
282
|
/** Sparkline narrative arc */
|
|
261
283
|
sparkline: SparklineStructure;
|
|
262
284
|
/** Key messages identified */
|
|
263
285
|
keyMessages: string[];
|
|
286
|
+
/** Data points (metrics, percentages, etc.) */
|
|
287
|
+
dataPoints: Array<{
|
|
288
|
+
value: string;
|
|
289
|
+
label: string;
|
|
290
|
+
context?: string;
|
|
291
|
+
}>;
|
|
264
292
|
/** Generated action titles */
|
|
265
293
|
titles: string[];
|
|
266
294
|
/** STAR moments identified */
|
|
@@ -611,7 +639,15 @@ declare class ContentAnalyzer {
|
|
|
611
639
|
private extractSCQA;
|
|
612
640
|
/**
|
|
613
641
|
* Extract STAR moments (Something They'll Always Remember)
|
|
614
|
-
* Per Nancy Duarte
|
|
642
|
+
* Per Nancy Duarte: These should be memorable, COMPLETE thoughts with impact
|
|
643
|
+
*
|
|
644
|
+
* CRITICAL REQUIREMENTS:
|
|
645
|
+
* - Must be complete sentences with subject + verb structure
|
|
646
|
+
* - Must have 5+ words minimum (no fragments!)
|
|
647
|
+
* - Must be 30+ characters
|
|
648
|
+
* - Must contain a verb
|
|
649
|
+
* - NOT fragments like "significant growth" or "cloud-first strategy"
|
|
650
|
+
* - NOT headers or topic labels
|
|
615
651
|
*/
|
|
616
652
|
private extractStarMoments;
|
|
617
653
|
/**
|
|
@@ -619,28 +655,27 @@ declare class ContentAnalyzer {
|
|
|
619
655
|
*/
|
|
620
656
|
private extractSparkline;
|
|
621
657
|
/**
|
|
622
|
-
* Extract key messages
|
|
658
|
+
* Extract key messages - the actual insights from the content
|
|
659
|
+
* Per Carmine Gallo: Rule of Three - max 3 key messages
|
|
660
|
+
* Per Barbara Minto: Messages should be actionable conclusions, not topics
|
|
623
661
|
*/
|
|
624
662
|
private extractKeyMessages;
|
|
625
663
|
/**
|
|
626
664
|
* Extract data points (metrics with values)
|
|
665
|
+
* IMPROVED: Smarter label extraction that understands markdown tables
|
|
627
666
|
*/
|
|
628
667
|
private extractDataPoints;
|
|
629
668
|
/**
|
|
630
|
-
*
|
|
669
|
+
* Extract a meaningful label from a line containing a metric
|
|
631
670
|
*/
|
|
632
|
-
private
|
|
633
|
-
/**
|
|
634
|
-
* Extract a label from surrounding context
|
|
635
|
-
* Fixes the "Century Interactive |" garbage issue by stripping table syntax
|
|
636
|
-
*/
|
|
637
|
-
private extractLabelFromContext;
|
|
671
|
+
private extractLabelFromLine;
|
|
638
672
|
/**
|
|
639
673
|
* Check if text contains any of the signals
|
|
640
674
|
*/
|
|
641
675
|
private containsSignals;
|
|
642
676
|
/**
|
|
643
677
|
* Extract first meaningful sentence from text
|
|
678
|
+
* CRITICAL: Must strip headers, CTA text, and fragments
|
|
644
679
|
*/
|
|
645
680
|
private extractFirstSentence;
|
|
646
681
|
}
|
|
@@ -664,8 +699,35 @@ declare class SlideFactory {
|
|
|
664
699
|
private isContentUsed;
|
|
665
700
|
/**
|
|
666
701
|
* Create slides from analyzed content.
|
|
702
|
+
*
|
|
703
|
+
* ARCHITECTURE (per KB expert methodologies):
|
|
704
|
+
* 1. Title slide - always first
|
|
705
|
+
* 2. Agenda slide - business mode only with 3+ sections
|
|
706
|
+
* 3. SCQA slides - Situation, Complication (per Minto)
|
|
707
|
+
* 4. Content slides - from sections with bullets/content
|
|
708
|
+
* 5. Metrics slide - if data points exist
|
|
709
|
+
* 6. Solution slide - SCQA answer
|
|
710
|
+
* 7. STAR moments - only if high-quality (per Duarte)
|
|
711
|
+
* 8. CTA slide - if call to action exists
|
|
712
|
+
* 9. Thank you slide - always last
|
|
667
713
|
*/
|
|
668
714
|
createSlides(analysis: ContentAnalysis$1, mode: PresentationMode$1): Promise<Slide[]>;
|
|
715
|
+
/**
|
|
716
|
+
* Create a slide from a section with bullets
|
|
717
|
+
*/
|
|
718
|
+
private createSectionBulletSlide;
|
|
719
|
+
/**
|
|
720
|
+
* Create a slide from a section with body content
|
|
721
|
+
*/
|
|
722
|
+
private createSectionContentSlide;
|
|
723
|
+
/**
|
|
724
|
+
* Extract first sentence from text
|
|
725
|
+
*/
|
|
726
|
+
private extractFirstSentence;
|
|
727
|
+
/**
|
|
728
|
+
* Create a metrics slide from data points
|
|
729
|
+
*/
|
|
730
|
+
private createMetricsSlide;
|
|
669
731
|
/**
|
|
670
732
|
* Create a title slide.
|
|
671
733
|
*/
|
|
@@ -707,11 +769,13 @@ declare class SlideFactory {
|
|
|
707
769
|
*/
|
|
708
770
|
private initializeTemplates;
|
|
709
771
|
/**
|
|
710
|
-
* Clean text by removing all content markers.
|
|
772
|
+
* Clean text by removing all markdown and content markers.
|
|
773
|
+
* CRITICAL: Must strip all formatting to prevent garbage in slides
|
|
711
774
|
*/
|
|
712
775
|
private cleanText;
|
|
713
776
|
/**
|
|
714
|
-
* Truncate text to max length at
|
|
777
|
+
* Truncate text to max length at sentence boundary when possible.
|
|
778
|
+
* CRITICAL: Never cut mid-number (99.5% should not become 99.)
|
|
715
779
|
*/
|
|
716
780
|
private truncate;
|
|
717
781
|
/**
|
|
@@ -723,7 +787,7 @@ declare class SlideFactory {
|
|
|
723
787
|
*/
|
|
724
788
|
private extractBullets;
|
|
725
789
|
/**
|
|
726
|
-
* Remove a statistic from text.
|
|
790
|
+
* Remove a statistic from text and clean thoroughly.
|
|
727
791
|
*/
|
|
728
792
|
private removeStatistic;
|
|
729
793
|
}
|
|
@@ -1359,4 +1423,4 @@ declare const _default: {
|
|
|
1359
1423
|
VERSION: string;
|
|
1360
1424
|
};
|
|
1361
1425
|
|
|
1362
|
-
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 ContentQAResults, type ContrastIssue, type ExpertQAResults, type ExpertValidation, type FontSizeIssue, type GlanceTestResult, type ImageData, type ImageProvider, type ImageRequest, type ImageResult, KnowledgeGateway, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, type SlideTemplate, type SlideType, type SlideVisualScore, type SparklineStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, UnsplashImageProvider, VERSION, ValidationError, type VisualQAResults, createDefaultChartProvider, createDefaultImageProvider, _default as default, generate, getKnowledgeGateway, validate };
|
|
1426
|
+
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 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, LocalImageProvider, MermaidProvider, type MetricData, type OneIdeaResult, type OutputFormat, PlaceholderImageProvider, PowerPointGenerator, type PresentationConfig, PresentationEngine, type PresentationMetadata, type PresentationMode$1 as PresentationMode, type PresentationResult, type PresentationType, QAEngine, QAFailureError, type QAIssue, type QAResults, QuickChartProvider, RevealJsGenerator, type SCQAStructure, ScoreCalculator, type SignalNoiseResult, type Slide, type SlideContentScore, type SlideData, SlideFactory, type SlideTemplate, type SlideType, type SlideVisualScore, type SparklineStructure, TemplateEngine, TemplateNotFoundError, type ThemeName, UnsplashImageProvider, VERSION, ValidationError, type VisualQAResults, createDefaultChartProvider, createDefaultImageProvider, _default as default, generate, getKnowledgeGateway, validate };
|
package/dist/index.js
CHANGED
|
@@ -657,27 +657,65 @@ var ContentAnalyzer = class {
|
|
|
657
657
|
}
|
|
658
658
|
/**
|
|
659
659
|
* Extract STAR moments (Something They'll Always Remember)
|
|
660
|
-
* Per Nancy Duarte
|
|
660
|
+
* Per Nancy Duarte: These should be memorable, COMPLETE thoughts with impact
|
|
661
|
+
*
|
|
662
|
+
* CRITICAL REQUIREMENTS:
|
|
663
|
+
* - Must be complete sentences with subject + verb structure
|
|
664
|
+
* - Must have 5+ words minimum (no fragments!)
|
|
665
|
+
* - Must be 30+ characters
|
|
666
|
+
* - Must contain a verb
|
|
667
|
+
* - NOT fragments like "significant growth" or "cloud-first strategy"
|
|
668
|
+
* - NOT headers or topic labels
|
|
661
669
|
*/
|
|
662
670
|
extractStarMoments(text) {
|
|
663
671
|
const starMoments = [];
|
|
664
|
-
const
|
|
672
|
+
const usedPhrases = /* @__PURE__ */ new Set();
|
|
673
|
+
const fragmentPatterns = [
|
|
674
|
+
/^[a-z]+-[a-z]+\s+(strategy|approach|solution|platform|architecture)$/i,
|
|
675
|
+
// "cloud-first strategy"
|
|
676
|
+
/^(significant|substantial|dramatic|rapid|strong)\s+(growth|increase|decline|change)$/i,
|
|
677
|
+
// "significant growth"
|
|
678
|
+
/^(digital|cloud|data|ai)\s+(transformation|strategy|platform|solution)$/i,
|
|
679
|
+
// "digital transformation"
|
|
680
|
+
/^(our|your|the|a)\s+\w+\s*$/i,
|
|
681
|
+
// "our solution", "the platform"
|
|
682
|
+
/^[a-z]+\s+[a-z]+$/i
|
|
683
|
+
// Any two-word phrase
|
|
684
|
+
];
|
|
685
|
+
const verbPatterns = /\b(is|are|was|were|will|can|could|should|must|has|have|had|does|do|provides?|enables?|allows?|offers?|delivers?|creates?|achieves?|exceeds?|results?|generates?|grows?|increases?|decreases?|transforms?|improves?|reduces?)\b/i;
|
|
686
|
+
const boldMatches = text.match(/\*\*([^*]+)\*\*/g);
|
|
665
687
|
if (boldMatches) {
|
|
666
|
-
for (const match of boldMatches
|
|
688
|
+
for (const match of boldMatches) {
|
|
667
689
|
const cleaned = match.replace(/\*\*/g, "").trim();
|
|
668
690
|
const lowerCleaned = cleaned.toLowerCase();
|
|
669
|
-
|
|
691
|
+
const wordCount = cleaned.split(/\s+/).length;
|
|
692
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
693
|
+
const hasVerb = verbPatterns.test(cleaned);
|
|
694
|
+
const isLongEnough = wordCount >= 5 && cleaned.length >= 30;
|
|
695
|
+
const isNotFragment = !fragmentPatterns.some((p) => p.test(cleaned));
|
|
696
|
+
const isNotHeader = !/^(The |Our |Your |Next |Key |Overview|Introduction|Conclusion|Summary|Background)/i.test(cleaned);
|
|
697
|
+
const isNotCTA = !this.containsSignals(lowerCleaned, this.ctaSignals);
|
|
698
|
+
const hasMetricContext = /\d+[%xX]|\$[\d,]+/.test(cleaned) && wordCount >= 5;
|
|
699
|
+
if ((hasVerb || hasMetricContext) && isLongEnough && isNotFragment && isNotHeader && isNotCTA && wordCount <= 25 && !usedPhrases.has(normalized)) {
|
|
670
700
|
starMoments.push(cleaned);
|
|
701
|
+
usedPhrases.add(normalized);
|
|
702
|
+
if (starMoments.length >= 3) break;
|
|
671
703
|
}
|
|
672
704
|
}
|
|
673
705
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const
|
|
679
|
-
|
|
706
|
+
if (starMoments.length < 3) {
|
|
707
|
+
const cleanedText = text.replace(/^#+\s+.+$/gm, "").replace(/\*\*/g, "").replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\n{2,}/g, "\n").trim();
|
|
708
|
+
const sentences = cleanedText.match(/[^.!?]+[.!?]/g) || [];
|
|
709
|
+
for (const sentence of sentences) {
|
|
710
|
+
const cleaned = sentence.trim();
|
|
711
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
712
|
+
const wordCount = cleaned.split(/\s+/).length;
|
|
713
|
+
const hasHighImpact = /(\d{2,3}%|\d+x|billion|million|\$[\d,]+\s*(million|billion)?)/i.test(cleaned);
|
|
714
|
+
const hasVerb = verbPatterns.test(cleaned);
|
|
715
|
+
if (hasHighImpact && hasVerb && wordCount >= 6 && wordCount <= 25 && cleaned.length >= 40 && cleaned.length <= 200 && !usedPhrases.has(normalized) && !this.containsSignals(cleaned.toLowerCase(), this.ctaSignals)) {
|
|
680
716
|
starMoments.push(cleaned);
|
|
717
|
+
usedPhrases.add(normalized);
|
|
718
|
+
if (starMoments.length >= 3) break;
|
|
681
719
|
}
|
|
682
720
|
}
|
|
683
721
|
}
|
|
@@ -699,28 +737,65 @@ var ContentAnalyzer = class {
|
|
|
699
737
|
return { callToAction };
|
|
700
738
|
}
|
|
701
739
|
/**
|
|
702
|
-
* Extract key messages
|
|
740
|
+
* Extract key messages - the actual insights from the content
|
|
741
|
+
* Per Carmine Gallo: Rule of Three - max 3 key messages
|
|
742
|
+
* Per Barbara Minto: Messages should be actionable conclusions, not topics
|
|
703
743
|
*/
|
|
704
744
|
extractKeyMessages(text) {
|
|
705
745
|
const messages = [];
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
746
|
+
const usedPhrases = /* @__PURE__ */ new Set();
|
|
747
|
+
let cleanText = text.replace(/^#+\s+.+$/gm, "").replace(/\[.+?\]/g, "").replace(/\*\*/g, "");
|
|
748
|
+
cleanText = cleanText.replace(/(\d)\.(\d)/g, "$1<DECIMAL>$2");
|
|
749
|
+
const rawSentences = cleanText.match(/[^.!?]+[.!?]/g) || [];
|
|
750
|
+
const sentences = rawSentences.map((s) => s.replace(/<DECIMAL>/g, "."));
|
|
751
|
+
const insightSignals = [
|
|
752
|
+
"we recommend",
|
|
753
|
+
"we suggest",
|
|
754
|
+
"our strategy",
|
|
755
|
+
"the solution",
|
|
756
|
+
"key finding",
|
|
757
|
+
"result",
|
|
758
|
+
"achieve",
|
|
759
|
+
"improve",
|
|
760
|
+
"increase",
|
|
761
|
+
"decrease",
|
|
762
|
+
"expect",
|
|
763
|
+
"roi",
|
|
764
|
+
"benefit",
|
|
765
|
+
"opportunity",
|
|
766
|
+
"transform"
|
|
767
|
+
];
|
|
768
|
+
for (const sentence of sentences) {
|
|
769
|
+
const cleaned = sentence.trim();
|
|
770
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
771
|
+
if (cleaned.length > 30 && cleaned.length < 150 && !usedPhrases.has(normalized) && !this.containsSignals(cleaned.toLowerCase(), this.ctaSignals)) {
|
|
772
|
+
if (this.containsSignals(cleaned.toLowerCase(), insightSignals)) {
|
|
773
|
+
messages.push(cleaned);
|
|
774
|
+
usedPhrases.add(normalized);
|
|
775
|
+
if (messages.length >= 3) break;
|
|
712
776
|
}
|
|
713
777
|
}
|
|
714
778
|
}
|
|
715
779
|
if (messages.length < 3) {
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
780
|
+
for (const sentence of sentences) {
|
|
781
|
+
const cleaned = sentence.trim();
|
|
782
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
783
|
+
if (cleaned.length > 25 && cleaned.length < 150 && !usedPhrases.has(normalized) && /\d+[%xX]|\$[\d,]+/.test(cleaned)) {
|
|
784
|
+
messages.push(cleaned);
|
|
785
|
+
usedPhrases.add(normalized);
|
|
786
|
+
if (messages.length >= 3) break;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if (messages.length < 2) {
|
|
791
|
+
for (const sentence of sentences) {
|
|
792
|
+
const cleaned = sentence.trim();
|
|
793
|
+
const wordCount = cleaned.split(/\s+/).length;
|
|
794
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
795
|
+
if (wordCount >= 6 && wordCount <= 25 && !usedPhrases.has(normalized) && /\b(is|are|will|can|should|must|has|have|provides?|enables?|allows?)\b/.test(cleaned.toLowerCase())) {
|
|
796
|
+
messages.push(cleaned);
|
|
797
|
+
usedPhrases.add(normalized);
|
|
798
|
+
if (messages.length >= 3) break;
|
|
724
799
|
}
|
|
725
800
|
}
|
|
726
801
|
}
|
|
@@ -728,51 +803,56 @@ var ContentAnalyzer = class {
|
|
|
728
803
|
}
|
|
729
804
|
/**
|
|
730
805
|
* Extract data points (metrics with values)
|
|
806
|
+
* IMPROVED: Smarter label extraction that understands markdown tables
|
|
731
807
|
*/
|
|
732
808
|
extractDataPoints(text) {
|
|
733
809
|
const dataPoints = [];
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
810
|
+
const usedValues = /* @__PURE__ */ new Set();
|
|
811
|
+
const tableLines = text.split("\n").filter((line) => line.includes("|"));
|
|
812
|
+
if (tableLines.length >= 3) {
|
|
813
|
+
const dataRows = tableLines.filter((line) => !line.match(/^[\s|:-]+$/));
|
|
814
|
+
if (dataRows.length >= 2) {
|
|
815
|
+
for (const row of dataRows.slice(1)) {
|
|
816
|
+
const cells = row.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
817
|
+
if (cells.length >= 2) {
|
|
818
|
+
const valueCell = cells.find((c) => /^\$?[\d,]+\.?\d*[MBK%]?$/.test(c.replace(/[,$]/g, "")));
|
|
819
|
+
const labelCell = cells[0];
|
|
820
|
+
if (valueCell && labelCell && !usedValues.has(valueCell)) {
|
|
821
|
+
usedValues.add(valueCell);
|
|
822
|
+
dataPoints.push({ value: valueCell, label: labelCell.slice(0, 40) });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
742
826
|
}
|
|
743
827
|
}
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
828
|
+
const lines = text.split("\n");
|
|
829
|
+
for (const line of lines) {
|
|
830
|
+
if (line.includes("|")) continue;
|
|
831
|
+
const percentMatch = line.match(/(\w+(?:\s+\w+){0,4})\s+(?:by\s+)?(\d+(?:\.\d+)?%)/i);
|
|
832
|
+
if (percentMatch && percentMatch[2] && percentMatch[1] && !usedValues.has(percentMatch[2])) {
|
|
833
|
+
usedValues.add(percentMatch[2]);
|
|
834
|
+
dataPoints.push({ value: percentMatch[2], label: percentMatch[1].slice(0, 40) });
|
|
835
|
+
}
|
|
836
|
+
const dollarMatch = line.match(/\$(\d+(?:\.\d+)?)\s*(million|billion|M|B|K)?/i);
|
|
837
|
+
if (dollarMatch && dataPoints.length < 6) {
|
|
838
|
+
const fullValue = "$" + dollarMatch[1] + (dollarMatch[2] ? " " + dollarMatch[2] : "");
|
|
839
|
+
if (!usedValues.has(fullValue)) {
|
|
840
|
+
usedValues.add(fullValue);
|
|
841
|
+
const label = this.extractLabelFromLine(line, fullValue);
|
|
842
|
+
dataPoints.push({ value: fullValue, label });
|
|
753
843
|
}
|
|
754
844
|
}
|
|
755
845
|
}
|
|
756
|
-
return dataPoints.slice(0,
|
|
846
|
+
return dataPoints.slice(0, 4);
|
|
757
847
|
}
|
|
758
848
|
/**
|
|
759
|
-
*
|
|
849
|
+
* Extract a meaningful label from a line containing a metric
|
|
760
850
|
*/
|
|
761
|
-
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
return text.slice(start, end);
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Extract a label from surrounding context
|
|
770
|
-
* Fixes the "Century Interactive |" garbage issue by stripping table syntax
|
|
771
|
-
*/
|
|
772
|
-
extractLabelFromContext(context) {
|
|
773
|
-
let cleaned = context.replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\s{2,}/g, " ").replace(/\*\*/g, "").replace(/#+\s*/g, "").trim();
|
|
774
|
-
const words = cleaned.split(/\s+/).filter((w) => w.length > 2 && !w.match(/^\d/));
|
|
775
|
-
return words.slice(0, 4).join(" ") || "Value";
|
|
851
|
+
extractLabelFromLine(line, value) {
|
|
852
|
+
const cleaned = line.replace(/\*\*/g, "").replace(/\|/g, " ").trim();
|
|
853
|
+
const beforeValue = cleaned.split(value)[0] || "";
|
|
854
|
+
const words = beforeValue.split(/\s+/).filter((w) => w.length > 2);
|
|
855
|
+
return words.slice(-4).join(" ").slice(0, 40) || "Value";
|
|
776
856
|
}
|
|
777
857
|
/**
|
|
778
858
|
* Check if text contains any of the signals
|
|
@@ -782,15 +862,20 @@ var ContentAnalyzer = class {
|
|
|
782
862
|
}
|
|
783
863
|
/**
|
|
784
864
|
* Extract first meaningful sentence from text
|
|
865
|
+
* CRITICAL: Must strip headers, CTA text, and fragments
|
|
785
866
|
*/
|
|
786
867
|
extractFirstSentence(text) {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
868
|
+
let cleaned = text.replace(/^#+\s+.+$/gm, "").replace(/\*\*/g, "").replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\n{2,}/g, "\n").trim();
|
|
869
|
+
cleaned = cleaned.replace(/^(Overview|Introduction|The Problem|Our Solution|Next Steps|Conclusion|Summary|Background|Context|Recommendation)\s*:?\s*/i, "").trim();
|
|
870
|
+
const sentences = cleaned.match(/[^.!?]+[.!?]/g) || [];
|
|
871
|
+
for (const sentence of sentences) {
|
|
872
|
+
const trimmed = sentence.trim();
|
|
873
|
+
if (trimmed.length >= 20 && /\b(is|are|was|were|will|can|should|must|has|have|had|provides?|enables?|allows?|offers?|delivers?|creates?|includes?|involves?|requires?|experiencing)\b/i.test(trimmed) && !this.containsSignals(trimmed.toLowerCase(), this.ctaSignals)) {
|
|
874
|
+
return trimmed.slice(0, 150);
|
|
875
|
+
}
|
|
792
876
|
}
|
|
793
|
-
|
|
877
|
+
const fallback = cleaned.slice(0, 150);
|
|
878
|
+
return fallback.length >= 20 ? fallback : "";
|
|
794
879
|
}
|
|
795
880
|
};
|
|
796
881
|
|
|
@@ -815,6 +900,17 @@ var SlideFactory = class {
|
|
|
815
900
|
}
|
|
816
901
|
/**
|
|
817
902
|
* Create slides from analyzed content.
|
|
903
|
+
*
|
|
904
|
+
* ARCHITECTURE (per KB expert methodologies):
|
|
905
|
+
* 1. Title slide - always first
|
|
906
|
+
* 2. Agenda slide - business mode only with 3+ sections
|
|
907
|
+
* 3. SCQA slides - Situation, Complication (per Minto)
|
|
908
|
+
* 4. Content slides - from sections with bullets/content
|
|
909
|
+
* 5. Metrics slide - if data points exist
|
|
910
|
+
* 6. Solution slide - SCQA answer
|
|
911
|
+
* 7. STAR moments - only if high-quality (per Duarte)
|
|
912
|
+
* 8. CTA slide - if call to action exists
|
|
913
|
+
* 9. Thank you slide - always last
|
|
818
914
|
*/
|
|
819
915
|
async createSlides(analysis, mode) {
|
|
820
916
|
const slides = [];
|
|
@@ -822,34 +918,121 @@ var SlideFactory = class {
|
|
|
822
918
|
this.usedContent.clear();
|
|
823
919
|
slides.push(this.createTitleSlide(slideIndex++, analysis));
|
|
824
920
|
this.isContentUsed(analysis.titles[0] ?? "");
|
|
825
|
-
|
|
921
|
+
const substantiveSections = analysis.sections.filter(
|
|
922
|
+
(s) => s.level === 2 && (s.bullets.length > 0 || s.content.length > 50)
|
|
923
|
+
);
|
|
924
|
+
if (mode === "business" && substantiveSections.length >= 3) {
|
|
826
925
|
slides.push(this.createAgendaSlide(slideIndex++, analysis));
|
|
827
926
|
}
|
|
828
|
-
if (analysis.scqa.situation && !this.isContentUsed(analysis.scqa.situation)) {
|
|
927
|
+
if (analysis.scqa.situation && analysis.scqa.situation.length > 30 && !this.isContentUsed(analysis.scqa.situation)) {
|
|
829
928
|
slides.push(this.createContextSlide(slideIndex++, analysis, mode));
|
|
830
929
|
}
|
|
831
|
-
if (analysis.scqa.complication && !this.isContentUsed(analysis.scqa.complication)) {
|
|
930
|
+
if (analysis.scqa.complication && analysis.scqa.complication.length > 30 && !this.isContentUsed(analysis.scqa.complication)) {
|
|
832
931
|
slides.push(this.createProblemSlide(slideIndex++, analysis, mode));
|
|
833
932
|
}
|
|
933
|
+
for (const section of substantiveSections.slice(0, 4)) {
|
|
934
|
+
const headerUsed = this.isContentUsed(section.header);
|
|
935
|
+
const contentUsed = section.content.length > 30 && this.isContentUsed(section.content.slice(0, 80));
|
|
936
|
+
if (!headerUsed && !contentUsed) {
|
|
937
|
+
if (section.bullets.length > 0) {
|
|
938
|
+
slides.push(this.createSectionBulletSlide(slideIndex++, section, mode));
|
|
939
|
+
} else if (section.content.length > 50) {
|
|
940
|
+
slides.push(this.createSectionContentSlide(slideIndex++, section, mode));
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
834
944
|
for (const message of analysis.keyMessages) {
|
|
835
|
-
|
|
945
|
+
const wordCount = message.split(/\s+/).length;
|
|
946
|
+
if (wordCount >= 6 && !/^(The |Our |Your |Overview|Introduction|Conclusion)/i.test(message) && !this.isContentUsed(message)) {
|
|
836
947
|
slides.push(this.createMessageSlide(slideIndex++, message, mode));
|
|
837
948
|
}
|
|
838
949
|
}
|
|
950
|
+
if (analysis.dataPoints.length >= 2) {
|
|
951
|
+
slides.push(this.createMetricsSlide(slideIndex++, analysis.dataPoints));
|
|
952
|
+
}
|
|
953
|
+
if (analysis.scqa.answer && analysis.scqa.answer.length > 30 && !this.isContentUsed(analysis.scqa.answer)) {
|
|
954
|
+
slides.push(this.createSolutionSlide(slideIndex++, analysis, mode));
|
|
955
|
+
}
|
|
956
|
+
const verbPattern = /\b(is|are|was|were|will|can|should|must|has|have|provides?|enables?|allows?|achieves?|exceeds?|results?|generates?|delivers?|creates?)\b/i;
|
|
839
957
|
for (const starMoment of analysis.starMoments.slice(0, 2)) {
|
|
840
|
-
|
|
958
|
+
const wordCount = starMoment.split(/\s+/).length;
|
|
959
|
+
const hasVerb = verbPattern.test(starMoment);
|
|
960
|
+
if (wordCount >= 6 && starMoment.length >= 40 && hasVerb && !this.isContentUsed(starMoment)) {
|
|
841
961
|
slides.push(this.createStarMomentSlide(slideIndex++, starMoment, mode));
|
|
842
962
|
}
|
|
843
963
|
}
|
|
844
|
-
if (analysis.
|
|
845
|
-
slides.push(this.createSolutionSlide(slideIndex++, analysis, mode));
|
|
846
|
-
}
|
|
847
|
-
if (analysis.sparkline.callToAdventure && !this.isContentUsed(analysis.sparkline.callToAdventure)) {
|
|
964
|
+
if (analysis.sparkline.callToAdventure && analysis.sparkline.callToAdventure.length > 20 && !this.isContentUsed(analysis.sparkline.callToAdventure)) {
|
|
848
965
|
slides.push(this.createCTASlide(slideIndex++, analysis, mode));
|
|
849
966
|
}
|
|
850
967
|
slides.push(this.createThankYouSlide(slideIndex++));
|
|
851
968
|
return slides;
|
|
852
969
|
}
|
|
970
|
+
/**
|
|
971
|
+
* Create a slide from a section with bullets
|
|
972
|
+
*/
|
|
973
|
+
createSectionBulletSlide(index, section, mode) {
|
|
974
|
+
const bullets = section.bullets.slice(0, mode === "keynote" ? 3 : 5);
|
|
975
|
+
return {
|
|
976
|
+
index,
|
|
977
|
+
type: "bullet-points",
|
|
978
|
+
data: {
|
|
979
|
+
title: this.truncate(section.header, 60),
|
|
980
|
+
bullets: bullets.map((b) => this.cleanText(b).slice(0, 80))
|
|
981
|
+
},
|
|
982
|
+
classes: ["slide-bullet-points"]
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Create a slide from a section with body content
|
|
987
|
+
*/
|
|
988
|
+
createSectionContentSlide(index, section, mode) {
|
|
989
|
+
if (mode === "keynote") {
|
|
990
|
+
return {
|
|
991
|
+
index,
|
|
992
|
+
type: "single-statement",
|
|
993
|
+
data: {
|
|
994
|
+
title: this.truncate(this.extractFirstSentence(section.content), 80),
|
|
995
|
+
keyMessage: section.header
|
|
996
|
+
},
|
|
997
|
+
classes: ["slide-single-statement"]
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
return {
|
|
1001
|
+
index,
|
|
1002
|
+
type: "two-column",
|
|
1003
|
+
data: {
|
|
1004
|
+
title: this.truncate(section.header, 60),
|
|
1005
|
+
body: this.truncate(section.content, 200)
|
|
1006
|
+
},
|
|
1007
|
+
classes: ["slide-two-column"]
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Extract first sentence from text
|
|
1012
|
+
*/
|
|
1013
|
+
extractFirstSentence(text) {
|
|
1014
|
+
const cleaned = this.cleanText(text);
|
|
1015
|
+
const match = cleaned.match(/^[^.!?]+[.!?]/);
|
|
1016
|
+
return match ? match[0].trim() : cleaned.slice(0, 100);
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Create a metrics slide from data points
|
|
1020
|
+
*/
|
|
1021
|
+
createMetricsSlide(index, dataPoints) {
|
|
1022
|
+
const metrics = dataPoints.slice(0, 4).map((dp) => ({
|
|
1023
|
+
value: dp.value,
|
|
1024
|
+
label: this.cleanText(dp.label).slice(0, 40)
|
|
1025
|
+
}));
|
|
1026
|
+
return {
|
|
1027
|
+
index,
|
|
1028
|
+
type: "metrics-grid",
|
|
1029
|
+
data: {
|
|
1030
|
+
title: "Key Metrics",
|
|
1031
|
+
metrics
|
|
1032
|
+
},
|
|
1033
|
+
classes: ["slide-metrics-grid"]
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
853
1036
|
/**
|
|
854
1037
|
* Create a title slide.
|
|
855
1038
|
*/
|
|
@@ -1146,23 +1329,43 @@ var SlideFactory = class {
|
|
|
1146
1329
|
}
|
|
1147
1330
|
// === Helper Methods ===
|
|
1148
1331
|
/**
|
|
1149
|
-
* Clean text by removing all content markers.
|
|
1332
|
+
* Clean text by removing all markdown and content markers.
|
|
1333
|
+
* CRITICAL: Must strip all formatting to prevent garbage in slides
|
|
1150
1334
|
*/
|
|
1151
1335
|
cleanText(text) {
|
|
1152
1336
|
if (!text) return "";
|
|
1153
|
-
return text.replace(/\[HEADER\]\s*/g, "").replace(/\[BULLET\]\s*/g, "").replace(/\[NUMBERED\]\s*/g, "").replace(/\[EMPHASIS\]/g, "").replace(/\[\/EMPHASIS\]/g, "").replace(/\[CODE BLOCK\]/g, "").replace(/\[IMAGE\]/g, "").replace(/\s+/g, " ").trim();
|
|
1337
|
+
return text.replace(/^#+\s+/gm, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/_([^_]+)_/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\[HEADER\]\s*/g, "").replace(/\[BULLET\]\s*/g, "").replace(/\[NUMBERED\]\s*/g, "").replace(/\[EMPHASIS\]/g, "").replace(/\[\/EMPHASIS\]/g, "").replace(/\[CODE BLOCK\]/g, "").replace(/\[IMAGE\]/g, "").replace(/\n{2,}/g, " ").replace(/\s+/g, " ").trim();
|
|
1154
1338
|
}
|
|
1155
1339
|
/**
|
|
1156
|
-
* Truncate text to max length at
|
|
1340
|
+
* Truncate text to max length at sentence boundary when possible.
|
|
1341
|
+
* CRITICAL: Never cut mid-number (99.5% should not become 99.)
|
|
1157
1342
|
*/
|
|
1158
1343
|
truncate(text, maxLength) {
|
|
1159
1344
|
const cleanedText = this.cleanText(text);
|
|
1160
1345
|
if (!cleanedText || cleanedText.length <= maxLength) {
|
|
1161
1346
|
return cleanedText;
|
|
1162
1347
|
}
|
|
1348
|
+
const sentences = cleanedText.match(/[^.!?]+[.!?]/g);
|
|
1349
|
+
if (sentences) {
|
|
1350
|
+
let result = "";
|
|
1351
|
+
for (const sentence of sentences) {
|
|
1352
|
+
if ((result + sentence).length <= maxLength) {
|
|
1353
|
+
result += sentence;
|
|
1354
|
+
} else {
|
|
1355
|
+
break;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
if (result.length > maxLength * 0.5) {
|
|
1359
|
+
return result.trim();
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1163
1362
|
const truncated = cleanedText.slice(0, maxLength);
|
|
1164
|
-
|
|
1165
|
-
|
|
1363
|
+
let lastSpace = truncated.lastIndexOf(" ");
|
|
1364
|
+
const afterCut = cleanedText.slice(lastSpace + 1, maxLength + 10);
|
|
1365
|
+
if (/^[\d.,%$]+/.test(afterCut)) {
|
|
1366
|
+
lastSpace = truncated.slice(0, lastSpace).lastIndexOf(" ");
|
|
1367
|
+
}
|
|
1368
|
+
if (lastSpace > maxLength * 0.5) {
|
|
1166
1369
|
return truncated.slice(0, lastSpace) + "...";
|
|
1167
1370
|
}
|
|
1168
1371
|
return truncated + "...";
|
|
@@ -1193,10 +1396,12 @@ var SlideFactory = class {
|
|
|
1193
1396
|
return sentences.slice(0, 5).map((s) => s.trim());
|
|
1194
1397
|
}
|
|
1195
1398
|
/**
|
|
1196
|
-
* Remove a statistic from text.
|
|
1399
|
+
* Remove a statistic from text and clean thoroughly.
|
|
1197
1400
|
*/
|
|
1198
1401
|
removeStatistic(text, stat) {
|
|
1199
|
-
|
|
1402
|
+
const cleaned = this.cleanText(text).replace(stat, "").replace(/^\s*[-–—:,]\s*/, "").trim();
|
|
1403
|
+
const firstSentence = cleaned.match(/^[^.!?]+[.!?]?/);
|
|
1404
|
+
return firstSentence ? firstSentence[0].slice(0, 80) : cleaned.slice(0, 80);
|
|
1200
1405
|
}
|
|
1201
1406
|
};
|
|
1202
1407
|
|
package/dist/index.mjs
CHANGED
|
@@ -594,27 +594,65 @@ var ContentAnalyzer = class {
|
|
|
594
594
|
}
|
|
595
595
|
/**
|
|
596
596
|
* Extract STAR moments (Something They'll Always Remember)
|
|
597
|
-
* Per Nancy Duarte
|
|
597
|
+
* Per Nancy Duarte: These should be memorable, COMPLETE thoughts with impact
|
|
598
|
+
*
|
|
599
|
+
* CRITICAL REQUIREMENTS:
|
|
600
|
+
* - Must be complete sentences with subject + verb structure
|
|
601
|
+
* - Must have 5+ words minimum (no fragments!)
|
|
602
|
+
* - Must be 30+ characters
|
|
603
|
+
* - Must contain a verb
|
|
604
|
+
* - NOT fragments like "significant growth" or "cloud-first strategy"
|
|
605
|
+
* - NOT headers or topic labels
|
|
598
606
|
*/
|
|
599
607
|
extractStarMoments(text) {
|
|
600
608
|
const starMoments = [];
|
|
601
|
-
const
|
|
609
|
+
const usedPhrases = /* @__PURE__ */ new Set();
|
|
610
|
+
const fragmentPatterns = [
|
|
611
|
+
/^[a-z]+-[a-z]+\s+(strategy|approach|solution|platform|architecture)$/i,
|
|
612
|
+
// "cloud-first strategy"
|
|
613
|
+
/^(significant|substantial|dramatic|rapid|strong)\s+(growth|increase|decline|change)$/i,
|
|
614
|
+
// "significant growth"
|
|
615
|
+
/^(digital|cloud|data|ai)\s+(transformation|strategy|platform|solution)$/i,
|
|
616
|
+
// "digital transformation"
|
|
617
|
+
/^(our|your|the|a)\s+\w+\s*$/i,
|
|
618
|
+
// "our solution", "the platform"
|
|
619
|
+
/^[a-z]+\s+[a-z]+$/i
|
|
620
|
+
// Any two-word phrase
|
|
621
|
+
];
|
|
622
|
+
const verbPatterns = /\b(is|are|was|were|will|can|could|should|must|has|have|had|does|do|provides?|enables?|allows?|offers?|delivers?|creates?|achieves?|exceeds?|results?|generates?|grows?|increases?|decreases?|transforms?|improves?|reduces?)\b/i;
|
|
623
|
+
const boldMatches = text.match(/\*\*([^*]+)\*\*/g);
|
|
602
624
|
if (boldMatches) {
|
|
603
|
-
for (const match of boldMatches
|
|
625
|
+
for (const match of boldMatches) {
|
|
604
626
|
const cleaned = match.replace(/\*\*/g, "").trim();
|
|
605
627
|
const lowerCleaned = cleaned.toLowerCase();
|
|
606
|
-
|
|
628
|
+
const wordCount = cleaned.split(/\s+/).length;
|
|
629
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
630
|
+
const hasVerb = verbPatterns.test(cleaned);
|
|
631
|
+
const isLongEnough = wordCount >= 5 && cleaned.length >= 30;
|
|
632
|
+
const isNotFragment = !fragmentPatterns.some((p) => p.test(cleaned));
|
|
633
|
+
const isNotHeader = !/^(The |Our |Your |Next |Key |Overview|Introduction|Conclusion|Summary|Background)/i.test(cleaned);
|
|
634
|
+
const isNotCTA = !this.containsSignals(lowerCleaned, this.ctaSignals);
|
|
635
|
+
const hasMetricContext = /\d+[%xX]|\$[\d,]+/.test(cleaned) && wordCount >= 5;
|
|
636
|
+
if ((hasVerb || hasMetricContext) && isLongEnough && isNotFragment && isNotHeader && isNotCTA && wordCount <= 25 && !usedPhrases.has(normalized)) {
|
|
607
637
|
starMoments.push(cleaned);
|
|
638
|
+
usedPhrases.add(normalized);
|
|
639
|
+
if (starMoments.length >= 3) break;
|
|
608
640
|
}
|
|
609
641
|
}
|
|
610
642
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const
|
|
616
|
-
|
|
643
|
+
if (starMoments.length < 3) {
|
|
644
|
+
const cleanedText = text.replace(/^#+\s+.+$/gm, "").replace(/\*\*/g, "").replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\n{2,}/g, "\n").trim();
|
|
645
|
+
const sentences = cleanedText.match(/[^.!?]+[.!?]/g) || [];
|
|
646
|
+
for (const sentence of sentences) {
|
|
647
|
+
const cleaned = sentence.trim();
|
|
648
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
649
|
+
const wordCount = cleaned.split(/\s+/).length;
|
|
650
|
+
const hasHighImpact = /(\d{2,3}%|\d+x|billion|million|\$[\d,]+\s*(million|billion)?)/i.test(cleaned);
|
|
651
|
+
const hasVerb = verbPatterns.test(cleaned);
|
|
652
|
+
if (hasHighImpact && hasVerb && wordCount >= 6 && wordCount <= 25 && cleaned.length >= 40 && cleaned.length <= 200 && !usedPhrases.has(normalized) && !this.containsSignals(cleaned.toLowerCase(), this.ctaSignals)) {
|
|
617
653
|
starMoments.push(cleaned);
|
|
654
|
+
usedPhrases.add(normalized);
|
|
655
|
+
if (starMoments.length >= 3) break;
|
|
618
656
|
}
|
|
619
657
|
}
|
|
620
658
|
}
|
|
@@ -636,28 +674,65 @@ var ContentAnalyzer = class {
|
|
|
636
674
|
return { callToAction };
|
|
637
675
|
}
|
|
638
676
|
/**
|
|
639
|
-
* Extract key messages
|
|
677
|
+
* Extract key messages - the actual insights from the content
|
|
678
|
+
* Per Carmine Gallo: Rule of Three - max 3 key messages
|
|
679
|
+
* Per Barbara Minto: Messages should be actionable conclusions, not topics
|
|
640
680
|
*/
|
|
641
681
|
extractKeyMessages(text) {
|
|
642
682
|
const messages = [];
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
683
|
+
const usedPhrases = /* @__PURE__ */ new Set();
|
|
684
|
+
let cleanText = text.replace(/^#+\s+.+$/gm, "").replace(/\[.+?\]/g, "").replace(/\*\*/g, "");
|
|
685
|
+
cleanText = cleanText.replace(/(\d)\.(\d)/g, "$1<DECIMAL>$2");
|
|
686
|
+
const rawSentences = cleanText.match(/[^.!?]+[.!?]/g) || [];
|
|
687
|
+
const sentences = rawSentences.map((s) => s.replace(/<DECIMAL>/g, "."));
|
|
688
|
+
const insightSignals = [
|
|
689
|
+
"we recommend",
|
|
690
|
+
"we suggest",
|
|
691
|
+
"our strategy",
|
|
692
|
+
"the solution",
|
|
693
|
+
"key finding",
|
|
694
|
+
"result",
|
|
695
|
+
"achieve",
|
|
696
|
+
"improve",
|
|
697
|
+
"increase",
|
|
698
|
+
"decrease",
|
|
699
|
+
"expect",
|
|
700
|
+
"roi",
|
|
701
|
+
"benefit",
|
|
702
|
+
"opportunity",
|
|
703
|
+
"transform"
|
|
704
|
+
];
|
|
705
|
+
for (const sentence of sentences) {
|
|
706
|
+
const cleaned = sentence.trim();
|
|
707
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
708
|
+
if (cleaned.length > 30 && cleaned.length < 150 && !usedPhrases.has(normalized) && !this.containsSignals(cleaned.toLowerCase(), this.ctaSignals)) {
|
|
709
|
+
if (this.containsSignals(cleaned.toLowerCase(), insightSignals)) {
|
|
710
|
+
messages.push(cleaned);
|
|
711
|
+
usedPhrases.add(normalized);
|
|
712
|
+
if (messages.length >= 3) break;
|
|
649
713
|
}
|
|
650
714
|
}
|
|
651
715
|
}
|
|
652
716
|
if (messages.length < 3) {
|
|
653
|
-
const
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
717
|
+
for (const sentence of sentences) {
|
|
718
|
+
const cleaned = sentence.trim();
|
|
719
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
720
|
+
if (cleaned.length > 25 && cleaned.length < 150 && !usedPhrases.has(normalized) && /\d+[%xX]|\$[\d,]+/.test(cleaned)) {
|
|
721
|
+
messages.push(cleaned);
|
|
722
|
+
usedPhrases.add(normalized);
|
|
723
|
+
if (messages.length >= 3) break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (messages.length < 2) {
|
|
728
|
+
for (const sentence of sentences) {
|
|
729
|
+
const cleaned = sentence.trim();
|
|
730
|
+
const wordCount = cleaned.split(/\s+/).length;
|
|
731
|
+
const normalized = cleaned.toLowerCase().slice(0, 30);
|
|
732
|
+
if (wordCount >= 6 && wordCount <= 25 && !usedPhrases.has(normalized) && /\b(is|are|will|can|should|must|has|have|provides?|enables?|allows?)\b/.test(cleaned.toLowerCase())) {
|
|
733
|
+
messages.push(cleaned);
|
|
734
|
+
usedPhrases.add(normalized);
|
|
735
|
+
if (messages.length >= 3) break;
|
|
661
736
|
}
|
|
662
737
|
}
|
|
663
738
|
}
|
|
@@ -665,51 +740,56 @@ var ContentAnalyzer = class {
|
|
|
665
740
|
}
|
|
666
741
|
/**
|
|
667
742
|
* Extract data points (metrics with values)
|
|
743
|
+
* IMPROVED: Smarter label extraction that understands markdown tables
|
|
668
744
|
*/
|
|
669
745
|
extractDataPoints(text) {
|
|
670
746
|
const dataPoints = [];
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
747
|
+
const usedValues = /* @__PURE__ */ new Set();
|
|
748
|
+
const tableLines = text.split("\n").filter((line) => line.includes("|"));
|
|
749
|
+
if (tableLines.length >= 3) {
|
|
750
|
+
const dataRows = tableLines.filter((line) => !line.match(/^[\s|:-]+$/));
|
|
751
|
+
if (dataRows.length >= 2) {
|
|
752
|
+
for (const row of dataRows.slice(1)) {
|
|
753
|
+
const cells = row.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
754
|
+
if (cells.length >= 2) {
|
|
755
|
+
const valueCell = cells.find((c) => /^\$?[\d,]+\.?\d*[MBK%]?$/.test(c.replace(/[,$]/g, "")));
|
|
756
|
+
const labelCell = cells[0];
|
|
757
|
+
if (valueCell && labelCell && !usedValues.has(valueCell)) {
|
|
758
|
+
usedValues.add(valueCell);
|
|
759
|
+
dataPoints.push({ value: valueCell, label: labelCell.slice(0, 40) });
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
679
763
|
}
|
|
680
764
|
}
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
765
|
+
const lines = text.split("\n");
|
|
766
|
+
for (const line of lines) {
|
|
767
|
+
if (line.includes("|")) continue;
|
|
768
|
+
const percentMatch = line.match(/(\w+(?:\s+\w+){0,4})\s+(?:by\s+)?(\d+(?:\.\d+)?%)/i);
|
|
769
|
+
if (percentMatch && percentMatch[2] && percentMatch[1] && !usedValues.has(percentMatch[2])) {
|
|
770
|
+
usedValues.add(percentMatch[2]);
|
|
771
|
+
dataPoints.push({ value: percentMatch[2], label: percentMatch[1].slice(0, 40) });
|
|
772
|
+
}
|
|
773
|
+
const dollarMatch = line.match(/\$(\d+(?:\.\d+)?)\s*(million|billion|M|B|K)?/i);
|
|
774
|
+
if (dollarMatch && dataPoints.length < 6) {
|
|
775
|
+
const fullValue = "$" + dollarMatch[1] + (dollarMatch[2] ? " " + dollarMatch[2] : "");
|
|
776
|
+
if (!usedValues.has(fullValue)) {
|
|
777
|
+
usedValues.add(fullValue);
|
|
778
|
+
const label = this.extractLabelFromLine(line, fullValue);
|
|
779
|
+
dataPoints.push({ value: fullValue, label });
|
|
690
780
|
}
|
|
691
781
|
}
|
|
692
782
|
}
|
|
693
|
-
return dataPoints.slice(0,
|
|
783
|
+
return dataPoints.slice(0, 4);
|
|
694
784
|
}
|
|
695
785
|
/**
|
|
696
|
-
*
|
|
786
|
+
* Extract a meaningful label from a line containing a metric
|
|
697
787
|
*/
|
|
698
|
-
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
return text.slice(start, end);
|
|
704
|
-
}
|
|
705
|
-
/**
|
|
706
|
-
* Extract a label from surrounding context
|
|
707
|
-
* Fixes the "Century Interactive |" garbage issue by stripping table syntax
|
|
708
|
-
*/
|
|
709
|
-
extractLabelFromContext(context) {
|
|
710
|
-
let cleaned = context.replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\s{2,}/g, " ").replace(/\*\*/g, "").replace(/#+\s*/g, "").trim();
|
|
711
|
-
const words = cleaned.split(/\s+/).filter((w) => w.length > 2 && !w.match(/^\d/));
|
|
712
|
-
return words.slice(0, 4).join(" ") || "Value";
|
|
788
|
+
extractLabelFromLine(line, value) {
|
|
789
|
+
const cleaned = line.replace(/\*\*/g, "").replace(/\|/g, " ").trim();
|
|
790
|
+
const beforeValue = cleaned.split(value)[0] || "";
|
|
791
|
+
const words = beforeValue.split(/\s+/).filter((w) => w.length > 2);
|
|
792
|
+
return words.slice(-4).join(" ").slice(0, 40) || "Value";
|
|
713
793
|
}
|
|
714
794
|
/**
|
|
715
795
|
* Check if text contains any of the signals
|
|
@@ -719,15 +799,20 @@ var ContentAnalyzer = class {
|
|
|
719
799
|
}
|
|
720
800
|
/**
|
|
721
801
|
* Extract first meaningful sentence from text
|
|
802
|
+
* CRITICAL: Must strip headers, CTA text, and fragments
|
|
722
803
|
*/
|
|
723
804
|
extractFirstSentence(text) {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
805
|
+
let cleaned = text.replace(/^#+\s+.+$/gm, "").replace(/\*\*/g, "").replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\n{2,}/g, "\n").trim();
|
|
806
|
+
cleaned = cleaned.replace(/^(Overview|Introduction|The Problem|Our Solution|Next Steps|Conclusion|Summary|Background|Context|Recommendation)\s*:?\s*/i, "").trim();
|
|
807
|
+
const sentences = cleaned.match(/[^.!?]+[.!?]/g) || [];
|
|
808
|
+
for (const sentence of sentences) {
|
|
809
|
+
const trimmed = sentence.trim();
|
|
810
|
+
if (trimmed.length >= 20 && /\b(is|are|was|were|will|can|should|must|has|have|had|provides?|enables?|allows?|offers?|delivers?|creates?|includes?|involves?|requires?|experiencing)\b/i.test(trimmed) && !this.containsSignals(trimmed.toLowerCase(), this.ctaSignals)) {
|
|
811
|
+
return trimmed.slice(0, 150);
|
|
812
|
+
}
|
|
729
813
|
}
|
|
730
|
-
|
|
814
|
+
const fallback = cleaned.slice(0, 150);
|
|
815
|
+
return fallback.length >= 20 ? fallback : "";
|
|
731
816
|
}
|
|
732
817
|
};
|
|
733
818
|
|
|
@@ -752,6 +837,17 @@ var SlideFactory = class {
|
|
|
752
837
|
}
|
|
753
838
|
/**
|
|
754
839
|
* Create slides from analyzed content.
|
|
840
|
+
*
|
|
841
|
+
* ARCHITECTURE (per KB expert methodologies):
|
|
842
|
+
* 1. Title slide - always first
|
|
843
|
+
* 2. Agenda slide - business mode only with 3+ sections
|
|
844
|
+
* 3. SCQA slides - Situation, Complication (per Minto)
|
|
845
|
+
* 4. Content slides - from sections with bullets/content
|
|
846
|
+
* 5. Metrics slide - if data points exist
|
|
847
|
+
* 6. Solution slide - SCQA answer
|
|
848
|
+
* 7. STAR moments - only if high-quality (per Duarte)
|
|
849
|
+
* 8. CTA slide - if call to action exists
|
|
850
|
+
* 9. Thank you slide - always last
|
|
755
851
|
*/
|
|
756
852
|
async createSlides(analysis, mode) {
|
|
757
853
|
const slides = [];
|
|
@@ -759,34 +855,121 @@ var SlideFactory = class {
|
|
|
759
855
|
this.usedContent.clear();
|
|
760
856
|
slides.push(this.createTitleSlide(slideIndex++, analysis));
|
|
761
857
|
this.isContentUsed(analysis.titles[0] ?? "");
|
|
762
|
-
|
|
858
|
+
const substantiveSections = analysis.sections.filter(
|
|
859
|
+
(s) => s.level === 2 && (s.bullets.length > 0 || s.content.length > 50)
|
|
860
|
+
);
|
|
861
|
+
if (mode === "business" && substantiveSections.length >= 3) {
|
|
763
862
|
slides.push(this.createAgendaSlide(slideIndex++, analysis));
|
|
764
863
|
}
|
|
765
|
-
if (analysis.scqa.situation && !this.isContentUsed(analysis.scqa.situation)) {
|
|
864
|
+
if (analysis.scqa.situation && analysis.scqa.situation.length > 30 && !this.isContentUsed(analysis.scqa.situation)) {
|
|
766
865
|
slides.push(this.createContextSlide(slideIndex++, analysis, mode));
|
|
767
866
|
}
|
|
768
|
-
if (analysis.scqa.complication && !this.isContentUsed(analysis.scqa.complication)) {
|
|
867
|
+
if (analysis.scqa.complication && analysis.scqa.complication.length > 30 && !this.isContentUsed(analysis.scqa.complication)) {
|
|
769
868
|
slides.push(this.createProblemSlide(slideIndex++, analysis, mode));
|
|
770
869
|
}
|
|
870
|
+
for (const section of substantiveSections.slice(0, 4)) {
|
|
871
|
+
const headerUsed = this.isContentUsed(section.header);
|
|
872
|
+
const contentUsed = section.content.length > 30 && this.isContentUsed(section.content.slice(0, 80));
|
|
873
|
+
if (!headerUsed && !contentUsed) {
|
|
874
|
+
if (section.bullets.length > 0) {
|
|
875
|
+
slides.push(this.createSectionBulletSlide(slideIndex++, section, mode));
|
|
876
|
+
} else if (section.content.length > 50) {
|
|
877
|
+
slides.push(this.createSectionContentSlide(slideIndex++, section, mode));
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
771
881
|
for (const message of analysis.keyMessages) {
|
|
772
|
-
|
|
882
|
+
const wordCount = message.split(/\s+/).length;
|
|
883
|
+
if (wordCount >= 6 && !/^(The |Our |Your |Overview|Introduction|Conclusion)/i.test(message) && !this.isContentUsed(message)) {
|
|
773
884
|
slides.push(this.createMessageSlide(slideIndex++, message, mode));
|
|
774
885
|
}
|
|
775
886
|
}
|
|
887
|
+
if (analysis.dataPoints.length >= 2) {
|
|
888
|
+
slides.push(this.createMetricsSlide(slideIndex++, analysis.dataPoints));
|
|
889
|
+
}
|
|
890
|
+
if (analysis.scqa.answer && analysis.scqa.answer.length > 30 && !this.isContentUsed(analysis.scqa.answer)) {
|
|
891
|
+
slides.push(this.createSolutionSlide(slideIndex++, analysis, mode));
|
|
892
|
+
}
|
|
893
|
+
const verbPattern = /\b(is|are|was|were|will|can|should|must|has|have|provides?|enables?|allows?|achieves?|exceeds?|results?|generates?|delivers?|creates?)\b/i;
|
|
776
894
|
for (const starMoment of analysis.starMoments.slice(0, 2)) {
|
|
777
|
-
|
|
895
|
+
const wordCount = starMoment.split(/\s+/).length;
|
|
896
|
+
const hasVerb = verbPattern.test(starMoment);
|
|
897
|
+
if (wordCount >= 6 && starMoment.length >= 40 && hasVerb && !this.isContentUsed(starMoment)) {
|
|
778
898
|
slides.push(this.createStarMomentSlide(slideIndex++, starMoment, mode));
|
|
779
899
|
}
|
|
780
900
|
}
|
|
781
|
-
if (analysis.
|
|
782
|
-
slides.push(this.createSolutionSlide(slideIndex++, analysis, mode));
|
|
783
|
-
}
|
|
784
|
-
if (analysis.sparkline.callToAdventure && !this.isContentUsed(analysis.sparkline.callToAdventure)) {
|
|
901
|
+
if (analysis.sparkline.callToAdventure && analysis.sparkline.callToAdventure.length > 20 && !this.isContentUsed(analysis.sparkline.callToAdventure)) {
|
|
785
902
|
slides.push(this.createCTASlide(slideIndex++, analysis, mode));
|
|
786
903
|
}
|
|
787
904
|
slides.push(this.createThankYouSlide(slideIndex++));
|
|
788
905
|
return slides;
|
|
789
906
|
}
|
|
907
|
+
/**
|
|
908
|
+
* Create a slide from a section with bullets
|
|
909
|
+
*/
|
|
910
|
+
createSectionBulletSlide(index, section, mode) {
|
|
911
|
+
const bullets = section.bullets.slice(0, mode === "keynote" ? 3 : 5);
|
|
912
|
+
return {
|
|
913
|
+
index,
|
|
914
|
+
type: "bullet-points",
|
|
915
|
+
data: {
|
|
916
|
+
title: this.truncate(section.header, 60),
|
|
917
|
+
bullets: bullets.map((b) => this.cleanText(b).slice(0, 80))
|
|
918
|
+
},
|
|
919
|
+
classes: ["slide-bullet-points"]
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Create a slide from a section with body content
|
|
924
|
+
*/
|
|
925
|
+
createSectionContentSlide(index, section, mode) {
|
|
926
|
+
if (mode === "keynote") {
|
|
927
|
+
return {
|
|
928
|
+
index,
|
|
929
|
+
type: "single-statement",
|
|
930
|
+
data: {
|
|
931
|
+
title: this.truncate(this.extractFirstSentence(section.content), 80),
|
|
932
|
+
keyMessage: section.header
|
|
933
|
+
},
|
|
934
|
+
classes: ["slide-single-statement"]
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
index,
|
|
939
|
+
type: "two-column",
|
|
940
|
+
data: {
|
|
941
|
+
title: this.truncate(section.header, 60),
|
|
942
|
+
body: this.truncate(section.content, 200)
|
|
943
|
+
},
|
|
944
|
+
classes: ["slide-two-column"]
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Extract first sentence from text
|
|
949
|
+
*/
|
|
950
|
+
extractFirstSentence(text) {
|
|
951
|
+
const cleaned = this.cleanText(text);
|
|
952
|
+
const match = cleaned.match(/^[^.!?]+[.!?]/);
|
|
953
|
+
return match ? match[0].trim() : cleaned.slice(0, 100);
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Create a metrics slide from data points
|
|
957
|
+
*/
|
|
958
|
+
createMetricsSlide(index, dataPoints) {
|
|
959
|
+
const metrics = dataPoints.slice(0, 4).map((dp) => ({
|
|
960
|
+
value: dp.value,
|
|
961
|
+
label: this.cleanText(dp.label).slice(0, 40)
|
|
962
|
+
}));
|
|
963
|
+
return {
|
|
964
|
+
index,
|
|
965
|
+
type: "metrics-grid",
|
|
966
|
+
data: {
|
|
967
|
+
title: "Key Metrics",
|
|
968
|
+
metrics
|
|
969
|
+
},
|
|
970
|
+
classes: ["slide-metrics-grid"]
|
|
971
|
+
};
|
|
972
|
+
}
|
|
790
973
|
/**
|
|
791
974
|
* Create a title slide.
|
|
792
975
|
*/
|
|
@@ -1083,23 +1266,43 @@ var SlideFactory = class {
|
|
|
1083
1266
|
}
|
|
1084
1267
|
// === Helper Methods ===
|
|
1085
1268
|
/**
|
|
1086
|
-
* Clean text by removing all content markers.
|
|
1269
|
+
* Clean text by removing all markdown and content markers.
|
|
1270
|
+
* CRITICAL: Must strip all formatting to prevent garbage in slides
|
|
1087
1271
|
*/
|
|
1088
1272
|
cleanText(text) {
|
|
1089
1273
|
if (!text) return "";
|
|
1090
|
-
return text.replace(/\[HEADER\]\s*/g, "").replace(/\[BULLET\]\s*/g, "").replace(/\[NUMBERED\]\s*/g, "").replace(/\[EMPHASIS\]/g, "").replace(/\[\/EMPHASIS\]/g, "").replace(/\[CODE BLOCK\]/g, "").replace(/\[IMAGE\]/g, "").replace(/\s+/g, " ").trim();
|
|
1274
|
+
return text.replace(/^#+\s+/gm, "").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/_([^_]+)_/g, "$1").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\|/g, " ").replace(/-{3,}/g, "").replace(/\[HEADER\]\s*/g, "").replace(/\[BULLET\]\s*/g, "").replace(/\[NUMBERED\]\s*/g, "").replace(/\[EMPHASIS\]/g, "").replace(/\[\/EMPHASIS\]/g, "").replace(/\[CODE BLOCK\]/g, "").replace(/\[IMAGE\]/g, "").replace(/\n{2,}/g, " ").replace(/\s+/g, " ").trim();
|
|
1091
1275
|
}
|
|
1092
1276
|
/**
|
|
1093
|
-
* Truncate text to max length at
|
|
1277
|
+
* Truncate text to max length at sentence boundary when possible.
|
|
1278
|
+
* CRITICAL: Never cut mid-number (99.5% should not become 99.)
|
|
1094
1279
|
*/
|
|
1095
1280
|
truncate(text, maxLength) {
|
|
1096
1281
|
const cleanedText = this.cleanText(text);
|
|
1097
1282
|
if (!cleanedText || cleanedText.length <= maxLength) {
|
|
1098
1283
|
return cleanedText;
|
|
1099
1284
|
}
|
|
1285
|
+
const sentences = cleanedText.match(/[^.!?]+[.!?]/g);
|
|
1286
|
+
if (sentences) {
|
|
1287
|
+
let result = "";
|
|
1288
|
+
for (const sentence of sentences) {
|
|
1289
|
+
if ((result + sentence).length <= maxLength) {
|
|
1290
|
+
result += sentence;
|
|
1291
|
+
} else {
|
|
1292
|
+
break;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (result.length > maxLength * 0.5) {
|
|
1296
|
+
return result.trim();
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1100
1299
|
const truncated = cleanedText.slice(0, maxLength);
|
|
1101
|
-
|
|
1102
|
-
|
|
1300
|
+
let lastSpace = truncated.lastIndexOf(" ");
|
|
1301
|
+
const afterCut = cleanedText.slice(lastSpace + 1, maxLength + 10);
|
|
1302
|
+
if (/^[\d.,%$]+/.test(afterCut)) {
|
|
1303
|
+
lastSpace = truncated.slice(0, lastSpace).lastIndexOf(" ");
|
|
1304
|
+
}
|
|
1305
|
+
if (lastSpace > maxLength * 0.5) {
|
|
1103
1306
|
return truncated.slice(0, lastSpace) + "...";
|
|
1104
1307
|
}
|
|
1105
1308
|
return truncated + "...";
|
|
@@ -1130,10 +1333,12 @@ var SlideFactory = class {
|
|
|
1130
1333
|
return sentences.slice(0, 5).map((s) => s.trim());
|
|
1131
1334
|
}
|
|
1132
1335
|
/**
|
|
1133
|
-
* Remove a statistic from text.
|
|
1336
|
+
* Remove a statistic from text and clean thoroughly.
|
|
1134
1337
|
*/
|
|
1135
1338
|
removeStatistic(text, stat) {
|
|
1136
|
-
|
|
1339
|
+
const cleaned = this.cleanText(text).replace(stat, "").replace(/^\s*[-–—:,]\s*/, "").trim();
|
|
1340
|
+
const firstSentence = cleaned.match(/^[^.!?]+[.!?]?/);
|
|
1341
|
+
return firstSentence ? firstSentence[0].slice(0, 80) : cleaned.slice(0, 80);
|
|
1137
1342
|
}
|
|
1138
1343
|
};
|
|
1139
1344
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-presentation-master",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.1.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",
|