claude-presentation-master 3.6.0 → 3.7.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/assets/presentation-knowledge.yaml +9 -9
- package/dist/index.d.mts +15 -5
- package/dist/index.d.ts +15 -5
- package/dist/index.js +169 -129
- package/dist/index.mjs +169 -129
- package/package.json +2 -2
|
@@ -3028,10 +3028,10 @@ persuasion_psychology:
|
|
|
3028
3028
|
principle: "Make abstract ideas tangible"
|
|
3029
3029
|
technique: "Use specific examples, not generalizations"
|
|
3030
3030
|
examples:
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3031
|
+
bad: "We improve operational efficiency"
|
|
3032
|
+
good: "We save 2 hours per employee per day"
|
|
3033
|
+
bad: "Significant ROI"
|
|
3034
|
+
good: "47% reduction in processing time, saving $2.3M annually"
|
|
3035
3035
|
|
|
3036
3036
|
credible:
|
|
3037
3037
|
principle: "Make people believe"
|
|
@@ -3051,10 +3051,10 @@ persuasion_psychology:
|
|
|
3051
3051
|
- "Use the power of one (individual stories)"
|
|
3052
3052
|
- "Connect to things people already care about"
|
|
3053
3053
|
examples:
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3054
|
+
bad: "This affects millions of people"
|
|
3055
|
+
good: "Meet Sarah. She represents your typical customer..."
|
|
3056
|
+
bad: "Revenue growth opportunity"
|
|
3057
|
+
good: "What would you do with an extra $2M?"
|
|
3058
3058
|
|
|
3059
3059
|
stories:
|
|
3060
3060
|
principle: "Tell stories, not facts"
|
|
@@ -3320,7 +3320,7 @@ data_integrity:
|
|
|
3320
3320
|
right: "[Cite specific evidence with source]"
|
|
3321
3321
|
imprecise_numbers:
|
|
3322
3322
|
wrong: "Approximately $5M"
|
|
3323
|
-
right:
|
|
3323
|
+
right: "$4.8M (Source: X)" or "$4.5-5.5M range (estimates)"
|
|
3324
3324
|
vague_trends:
|
|
3325
3325
|
wrong: "The market is growing"
|
|
3326
3326
|
right: "Market growing 12% CAGR (Gartner, 2024)"
|
package/dist/index.d.mts
CHANGED
|
@@ -548,6 +548,9 @@ declare class SlideGenerator {
|
|
|
548
548
|
private wordLimits;
|
|
549
549
|
private bulletLimit;
|
|
550
550
|
private allowedSlideTypes;
|
|
551
|
+
private createdSlideTitles;
|
|
552
|
+
private sectionDividerCount;
|
|
553
|
+
private readonly maxSectionDividers;
|
|
551
554
|
constructor();
|
|
552
555
|
initialize(): Promise<void>;
|
|
553
556
|
/**
|
|
@@ -605,14 +608,14 @@ declare class SlideGenerator {
|
|
|
605
608
|
*/
|
|
606
609
|
private stripMarkdownSyntax;
|
|
607
610
|
/**
|
|
608
|
-
* Clean bullets - strip markdown and
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
+
* Clean bullets - strip markdown and preserve COMPLETE, MEANINGFUL sentences.
|
|
612
|
+
* Business presentations need full context. Never truncate mid-thought.
|
|
613
|
+
* Reasonable limit: 20 words per bullet allows complete ideas.
|
|
611
614
|
*/
|
|
612
615
|
private cleanBullets;
|
|
613
616
|
/**
|
|
614
617
|
* Truncate text to word limit, stripping markdown first.
|
|
615
|
-
*
|
|
618
|
+
* Preserves complete sentences. Business content needs room to breathe.
|
|
616
619
|
*/
|
|
617
620
|
private truncateToWordLimit;
|
|
618
621
|
/**
|
|
@@ -626,6 +629,7 @@ declare class SlideGenerator {
|
|
|
626
629
|
private extractKeyMessage;
|
|
627
630
|
/**
|
|
628
631
|
* Extract a label from context (nearby text around a data point).
|
|
632
|
+
* CRITICAL: Strip all markdown table syntax first to avoid garbage labels.
|
|
629
633
|
*/
|
|
630
634
|
private extractLabelFromContext;
|
|
631
635
|
/**
|
|
@@ -1144,7 +1148,13 @@ declare class Renderer {
|
|
|
1144
1148
|
private escapeHTML;
|
|
1145
1149
|
/**
|
|
1146
1150
|
* Generate a background image URL based on slide type and content.
|
|
1147
|
-
* Priority: 1) slide.data.image, 2) local images array, 3)
|
|
1151
|
+
* Priority: 1) slide.data.image, 2) local images array, 3) NO RANDOM STOCK PHOTOS
|
|
1152
|
+
*
|
|
1153
|
+
* IMPORTANT: Random stock photos destroy credibility. A photo of flowers behind
|
|
1154
|
+
* a financial slide makes you look incompetent. Only use images that are:
|
|
1155
|
+
* - Explicitly provided by the user
|
|
1156
|
+
* - Actually relevant to the content
|
|
1157
|
+
* Otherwise, rely on clean gradients via CSS.
|
|
1148
1158
|
*/
|
|
1149
1159
|
private getBackgroundImageUrl;
|
|
1150
1160
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -548,6 +548,9 @@ declare class SlideGenerator {
|
|
|
548
548
|
private wordLimits;
|
|
549
549
|
private bulletLimit;
|
|
550
550
|
private allowedSlideTypes;
|
|
551
|
+
private createdSlideTitles;
|
|
552
|
+
private sectionDividerCount;
|
|
553
|
+
private readonly maxSectionDividers;
|
|
551
554
|
constructor();
|
|
552
555
|
initialize(): Promise<void>;
|
|
553
556
|
/**
|
|
@@ -605,14 +608,14 @@ declare class SlideGenerator {
|
|
|
605
608
|
*/
|
|
606
609
|
private stripMarkdownSyntax;
|
|
607
610
|
/**
|
|
608
|
-
* Clean bullets - strip markdown and
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
+
* Clean bullets - strip markdown and preserve COMPLETE, MEANINGFUL sentences.
|
|
612
|
+
* Business presentations need full context. Never truncate mid-thought.
|
|
613
|
+
* Reasonable limit: 20 words per bullet allows complete ideas.
|
|
611
614
|
*/
|
|
612
615
|
private cleanBullets;
|
|
613
616
|
/**
|
|
614
617
|
* Truncate text to word limit, stripping markdown first.
|
|
615
|
-
*
|
|
618
|
+
* Preserves complete sentences. Business content needs room to breathe.
|
|
616
619
|
*/
|
|
617
620
|
private truncateToWordLimit;
|
|
618
621
|
/**
|
|
@@ -626,6 +629,7 @@ declare class SlideGenerator {
|
|
|
626
629
|
private extractKeyMessage;
|
|
627
630
|
/**
|
|
628
631
|
* Extract a label from context (nearby text around a data point).
|
|
632
|
+
* CRITICAL: Strip all markdown table syntax first to avoid garbage labels.
|
|
629
633
|
*/
|
|
630
634
|
private extractLabelFromContext;
|
|
631
635
|
/**
|
|
@@ -1144,7 +1148,13 @@ declare class Renderer {
|
|
|
1144
1148
|
private escapeHTML;
|
|
1145
1149
|
/**
|
|
1146
1150
|
* Generate a background image URL based on slide type and content.
|
|
1147
|
-
* Priority: 1) slide.data.image, 2) local images array, 3)
|
|
1151
|
+
* Priority: 1) slide.data.image, 2) local images array, 3) NO RANDOM STOCK PHOTOS
|
|
1152
|
+
*
|
|
1153
|
+
* IMPORTANT: Random stock photos destroy credibility. A photo of flowers behind
|
|
1154
|
+
* a financial slide makes you look incompetent. Only use images that are:
|
|
1155
|
+
* - Explicitly provided by the user
|
|
1156
|
+
* - Actually relevant to the content
|
|
1157
|
+
* Otherwise, rely on clean gradients via CSS.
|
|
1148
1158
|
*/
|
|
1149
1159
|
private getBackgroundImageUrl;
|
|
1150
1160
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1498,6 +1498,12 @@ var SlideGenerator = class {
|
|
|
1498
1498
|
wordLimits;
|
|
1499
1499
|
bulletLimit;
|
|
1500
1500
|
allowedSlideTypes;
|
|
1501
|
+
// Track created slide titles to prevent duplicates
|
|
1502
|
+
createdSlideTitles = /* @__PURE__ */ new Set();
|
|
1503
|
+
// Track section dividers to avoid excessive blank slides
|
|
1504
|
+
sectionDividerCount = 0;
|
|
1505
|
+
maxSectionDividers = 4;
|
|
1506
|
+
// Only first 4 major sections get dividers
|
|
1501
1507
|
constructor() {
|
|
1502
1508
|
this.kb = getKB();
|
|
1503
1509
|
this.vds = getVisualDesignSystem();
|
|
@@ -1513,6 +1519,8 @@ var SlideGenerator = class {
|
|
|
1513
1519
|
*/
|
|
1514
1520
|
async generate(analysis, presentationType) {
|
|
1515
1521
|
await this.initialize();
|
|
1522
|
+
this.createdSlideTitles.clear();
|
|
1523
|
+
this.sectionDividerCount = 0;
|
|
1516
1524
|
this.loadRulesForType(presentationType);
|
|
1517
1525
|
const slides = [];
|
|
1518
1526
|
let slideIndex = 0;
|
|
@@ -1659,8 +1667,15 @@ var SlideGenerator = class {
|
|
|
1659
1667
|
*/
|
|
1660
1668
|
createSectionSlides(section, analysis, type, startIndex) {
|
|
1661
1669
|
const slides = [];
|
|
1670
|
+
const normalizedTitle = section.header.toLowerCase().trim();
|
|
1671
|
+
if (this.createdSlideTitles.has(normalizedTitle)) {
|
|
1672
|
+
return slides;
|
|
1673
|
+
}
|
|
1662
1674
|
const slideType = this.determineSlideType(section, type);
|
|
1663
|
-
|
|
1675
|
+
const hasSubstantiveContent = section.bullets.length > 0 || section.metrics.length > 0 || section.content.trim().length > 50;
|
|
1676
|
+
if (section.level === 2 && this.sectionDividerCount < this.maxSectionDividers && hasSubstantiveContent) {
|
|
1677
|
+
this.sectionDividerCount++;
|
|
1678
|
+
this.createdSlideTitles.add(normalizedTitle);
|
|
1664
1679
|
slides.push({
|
|
1665
1680
|
index: startIndex,
|
|
1666
1681
|
type: "section_divider",
|
|
@@ -1884,36 +1899,35 @@ var SlideGenerator = class {
|
|
|
1884
1899
|
return clean.trim();
|
|
1885
1900
|
}
|
|
1886
1901
|
/**
|
|
1887
|
-
* Clean bullets - strip markdown and
|
|
1888
|
-
*
|
|
1889
|
-
*
|
|
1902
|
+
* Clean bullets - strip markdown and preserve COMPLETE, MEANINGFUL sentences.
|
|
1903
|
+
* Business presentations need full context. Never truncate mid-thought.
|
|
1904
|
+
* Reasonable limit: 20 words per bullet allows complete ideas.
|
|
1890
1905
|
*/
|
|
1891
1906
|
cleanBullets(bullets) {
|
|
1892
|
-
const maxBullets = Math.min(this.bulletLimit,
|
|
1893
|
-
const maxWordsPerBullet =
|
|
1907
|
+
const maxBullets = Math.min(this.bulletLimit, 6);
|
|
1908
|
+
const maxWordsPerBullet = 20;
|
|
1894
1909
|
return bullets.slice(0, maxBullets).map((b) => {
|
|
1895
1910
|
const clean = this.stripMarkdownSyntax(b);
|
|
1896
1911
|
const words = clean.split(/\s+/).filter((w) => w.length > 0);
|
|
1897
1912
|
if (words.length <= maxWordsPerBullet) {
|
|
1898
1913
|
return clean;
|
|
1899
1914
|
}
|
|
1900
|
-
const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
}
|
|
1915
|
+
const truncated = words.slice(0, maxWordsPerBullet).join(" ");
|
|
1916
|
+
const lastPeriod = truncated.lastIndexOf(".");
|
|
1917
|
+
const lastColon = truncated.lastIndexOf(":");
|
|
1918
|
+
const lastDash = truncated.lastIndexOf(" \u2013 ");
|
|
1919
|
+
const lastHyphen = truncated.lastIndexOf(" - ");
|
|
1920
|
+
const breakPoints = [lastPeriod, lastColon, lastDash, lastHyphen].filter((p) => p > 15);
|
|
1921
|
+
const bestBreak = Math.max(...breakPoints, -1);
|
|
1922
|
+
if (bestBreak > 15) {
|
|
1923
|
+
return truncated.slice(0, bestBreak + 1).trim();
|
|
1910
1924
|
}
|
|
1911
|
-
return
|
|
1925
|
+
return truncated;
|
|
1912
1926
|
}).filter((b) => b.length > 0);
|
|
1913
1927
|
}
|
|
1914
1928
|
/**
|
|
1915
1929
|
* Truncate text to word limit, stripping markdown first.
|
|
1916
|
-
*
|
|
1930
|
+
* Preserves complete sentences. Business content needs room to breathe.
|
|
1917
1931
|
*/
|
|
1918
1932
|
truncateToWordLimit(text) {
|
|
1919
1933
|
const cleanText = this.stripMarkdownSyntax(text);
|
|
@@ -1921,18 +1935,18 @@ var SlideGenerator = class {
|
|
|
1921
1935
|
return "";
|
|
1922
1936
|
}
|
|
1923
1937
|
const words = cleanText.split(/\s+/).filter((w) => w.length > 0);
|
|
1924
|
-
const
|
|
1925
|
-
if (words.length <=
|
|
1938
|
+
const bodyLimit = Math.min(this.wordLimits.max || 60, 50);
|
|
1939
|
+
if (words.length <= bodyLimit) {
|
|
1926
1940
|
return cleanText;
|
|
1927
1941
|
}
|
|
1928
|
-
const truncatedWords = words.slice(0,
|
|
1942
|
+
const truncatedWords = words.slice(0, bodyLimit);
|
|
1929
1943
|
const truncatedText = truncatedWords.join(" ");
|
|
1930
1944
|
const lastSentenceEnd = Math.max(
|
|
1931
1945
|
truncatedText.lastIndexOf("."),
|
|
1932
1946
|
truncatedText.lastIndexOf("!"),
|
|
1933
1947
|
truncatedText.lastIndexOf("?")
|
|
1934
1948
|
);
|
|
1935
|
-
if (lastSentenceEnd >
|
|
1949
|
+
if (lastSentenceEnd > 20) {
|
|
1936
1950
|
return truncatedText.slice(0, lastSentenceEnd + 1).trim();
|
|
1937
1951
|
}
|
|
1938
1952
|
return truncatedText;
|
|
@@ -1971,18 +1985,30 @@ var SlideGenerator = class {
|
|
|
1971
1985
|
}
|
|
1972
1986
|
/**
|
|
1973
1987
|
* Extract a label from context (nearby text around a data point).
|
|
1988
|
+
* CRITICAL: Strip all markdown table syntax first to avoid garbage labels.
|
|
1974
1989
|
*/
|
|
1975
1990
|
extractLabelFromContext(context) {
|
|
1976
|
-
|
|
1991
|
+
let cleanContext = context.replace(/\|/g, " ").replace(/-{2,}/g, "").replace(/:{1,2}/g, "").replace(/\s{2,}/g, " ").trim();
|
|
1992
|
+
if (!cleanContext || cleanContext.length < 3) {
|
|
1993
|
+
return "Value";
|
|
1994
|
+
}
|
|
1995
|
+
const beforeMatch = cleanContext.match(/^([A-Za-z][A-Za-z\s&]+?)(?:\s*[:=]?\s*)?[\d$%]/);
|
|
1977
1996
|
const beforeGroup = beforeMatch?.[1];
|
|
1978
|
-
if (beforeGroup && beforeGroup.trim().length <
|
|
1997
|
+
if (beforeGroup && beforeGroup.trim().length >= 3 && beforeGroup.trim().length < 40) {
|
|
1979
1998
|
return beforeGroup.trim();
|
|
1980
1999
|
}
|
|
1981
|
-
const afterMatch =
|
|
2000
|
+
const afterMatch = cleanContext.match(/[\d$%]+[KMB]?\s*(?:in|for|of|per)?\s*([A-Za-z][A-Za-z\s&]+)$/i);
|
|
1982
2001
|
const afterGroup = afterMatch?.[1];
|
|
1983
|
-
if (afterGroup && afterGroup.trim().length <
|
|
2002
|
+
if (afterGroup && afterGroup.trim().length >= 3 && afterGroup.trim().length < 40) {
|
|
1984
2003
|
return afterGroup.trim().replace(/[.,]$/, "");
|
|
1985
2004
|
}
|
|
2005
|
+
const words = cleanContext.match(/[A-Za-z]{3,}/g);
|
|
2006
|
+
if (words && words.length > 0) {
|
|
2007
|
+
const label = words.slice(0, 3).join(" ");
|
|
2008
|
+
if (label.length >= 3 && label.length < 40) {
|
|
2009
|
+
return label;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
1986
2012
|
return "Metric";
|
|
1987
2013
|
}
|
|
1988
2014
|
/**
|
|
@@ -3740,194 +3766,196 @@ ${slides.map((slide, index) => this.renderSlide(slide, options.presentationType,
|
|
|
3740
3766
|
/* CSS Variables from Knowledge Base - ALL styling driven by KB */
|
|
3741
3767
|
:root {
|
|
3742
3768
|
${cssVariables}
|
|
3769
|
+
/* Professional color scheme - McKinsey/BCG style */
|
|
3770
|
+
--slide-bg-primary: #1a1a2e;
|
|
3771
|
+
--slide-bg-secondary: #16213e;
|
|
3772
|
+
--slide-bg-accent: #0f3460;
|
|
3773
|
+
--text-primary: #ffffff;
|
|
3774
|
+
--text-secondary: rgba(255, 255, 255, 0.85);
|
|
3775
|
+
--text-muted: rgba(255, 255, 255, 0.6);
|
|
3776
|
+
--accent-blue: #4a9eff;
|
|
3777
|
+
--accent-green: #00d4aa;
|
|
3778
|
+
--accent-orange: #ff9f43;
|
|
3743
3779
|
}
|
|
3744
3780
|
|
|
3745
|
-
/* Premium Slide Styling -
|
|
3781
|
+
/* Premium Slide Styling - Clean, Professional */
|
|
3746
3782
|
.reveal {
|
|
3747
|
-
font-family: var(--font-body, system-ui, sans-serif);
|
|
3748
|
-
background: var(--color-background);
|
|
3783
|
+
font-family: var(--font-body, 'Inter', system-ui, sans-serif);
|
|
3749
3784
|
}
|
|
3750
3785
|
|
|
3751
3786
|
.reveal .slides {
|
|
3752
3787
|
text-align: left;
|
|
3753
3788
|
}
|
|
3754
3789
|
|
|
3790
|
+
/* All slides get a professional dark gradient background */
|
|
3755
3791
|
.reveal .slides section {
|
|
3756
|
-
padding:
|
|
3792
|
+
padding: 60px 80px !important;
|
|
3757
3793
|
box-sizing: border-box !important;
|
|
3758
|
-
|
|
3794
|
+
background: linear-gradient(135deg, var(--slide-bg-primary) 0%, var(--slide-bg-secondary) 50%, var(--slide-bg-accent) 100%);
|
|
3759
3795
|
}
|
|
3760
3796
|
|
|
3761
|
-
/*
|
|
3762
|
-
.reveal .slides section::before {
|
|
3797
|
+
/* When slide has a background image, add overlay for text readability */
|
|
3798
|
+
.reveal .slides section[data-background-image]::before {
|
|
3763
3799
|
content: '';
|
|
3764
3800
|
position: absolute;
|
|
3765
3801
|
top: 0;
|
|
3766
3802
|
left: 0;
|
|
3767
3803
|
right: 0;
|
|
3768
3804
|
bottom: 0;
|
|
3769
|
-
|
|
3770
|
-
background: radial-gradient(ellipse at center,
|
|
3771
|
-
rgba(0, 0, 0, 0.35) 0%,
|
|
3772
|
-
rgba(0, 0, 0, 0.55) 100%);
|
|
3805
|
+
background: rgba(0, 0, 0, 0.5);
|
|
3773
3806
|
z-index: 0;
|
|
3774
|
-
pointer-events: none;
|
|
3775
3807
|
}
|
|
3776
3808
|
|
|
3777
|
-
|
|
3778
|
-
.reveal .slides section > * {
|
|
3809
|
+
.reveal .slides section[data-background-image] > * {
|
|
3779
3810
|
position: relative;
|
|
3780
3811
|
z-index: 1;
|
|
3781
3812
|
}
|
|
3782
3813
|
|
|
3783
|
-
/*
|
|
3784
|
-
.reveal .slides section h1,
|
|
3785
|
-
.reveal .slides section h2,
|
|
3786
|
-
.reveal .slides section h3,
|
|
3787
|
-
.reveal .slides section p,
|
|
3788
|
-
.reveal .slides section li,
|
|
3789
|
-
.reveal .slides section .column,
|
|
3790
|
-
.reveal .slides section .key-message,
|
|
3791
|
-
.reveal .slides section .subtitle,
|
|
3792
|
-
.reveal .slides section ul,
|
|
3793
|
-
.reveal .slides section .metric-value,
|
|
3794
|
-
.reveal .slides section .metric-label {
|
|
3795
|
-
position: relative;
|
|
3796
|
-
z-index: 10;
|
|
3797
|
-
color: var(--color-text, #ffffff) !important;
|
|
3798
|
-
}
|
|
3799
|
-
|
|
3800
|
-
/* Typography Hierarchy - All from KB */
|
|
3801
|
-
/* Text shadow on ALL text for guaranteed readability over any background */
|
|
3802
|
-
.reveal h1, .reveal h2, .reveal h3, .reveal p, .reveal li, .reveal .key-message, .reveal .subtitle {
|
|
3803
|
-
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9), 0 0 30px rgba(0, 0, 0, 0.7);
|
|
3804
|
-
}
|
|
3805
|
-
|
|
3814
|
+
/* Typography - Clean, readable, professional */
|
|
3806
3815
|
.reveal h1 {
|
|
3807
|
-
font-family: var(--font-title,
|
|
3808
|
-
font-size:
|
|
3809
|
-
font-weight:
|
|
3810
|
-
color: var(--
|
|
3811
|
-
margin-bottom:
|
|
3812
|
-
line-height:
|
|
3816
|
+
font-family: var(--font-title, 'Inter', system-ui, sans-serif);
|
|
3817
|
+
font-size: 56px;
|
|
3818
|
+
font-weight: 700;
|
|
3819
|
+
color: var(--text-primary);
|
|
3820
|
+
margin-bottom: 24px;
|
|
3821
|
+
line-height: 1.1;
|
|
3822
|
+
letter-spacing: -0.02em;
|
|
3813
3823
|
}
|
|
3814
3824
|
|
|
3815
3825
|
.reveal h2 {
|
|
3816
|
-
font-family: var(--font-title,
|
|
3817
|
-
font-size:
|
|
3818
|
-
font-weight:
|
|
3819
|
-
color: var(--
|
|
3820
|
-
|
|
3821
|
-
|
|
3826
|
+
font-family: var(--font-title, 'Inter', system-ui, sans-serif);
|
|
3827
|
+
font-size: 36px;
|
|
3828
|
+
font-weight: 600;
|
|
3829
|
+
color: var(--text-secondary);
|
|
3830
|
+
margin-bottom: 32px;
|
|
3831
|
+
line-height: 1.2;
|
|
3822
3832
|
}
|
|
3823
3833
|
|
|
3824
3834
|
.reveal h3 {
|
|
3825
|
-
font-
|
|
3826
|
-
font-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
margin-bottom: var(--spacing-md);
|
|
3835
|
+
font-size: 28px;
|
|
3836
|
+
font-weight: 600;
|
|
3837
|
+
color: var(--text-primary);
|
|
3838
|
+
margin-bottom: 16px;
|
|
3830
3839
|
}
|
|
3831
3840
|
|
|
3832
3841
|
.reveal p {
|
|
3833
|
-
font-size:
|
|
3834
|
-
color: var(--
|
|
3835
|
-
line-height:
|
|
3836
|
-
margin-bottom:
|
|
3842
|
+
font-size: 22px;
|
|
3843
|
+
color: var(--text-secondary);
|
|
3844
|
+
line-height: 1.6;
|
|
3845
|
+
margin-bottom: 24px;
|
|
3837
3846
|
}
|
|
3838
3847
|
|
|
3839
|
-
/* Bullet Points -
|
|
3848
|
+
/* Bullet Points - Professional styling */
|
|
3840
3849
|
.reveal ul, .reveal ol {
|
|
3841
|
-
margin-left:
|
|
3850
|
+
margin-left: 0;
|
|
3851
|
+
padding-left: 0;
|
|
3852
|
+
list-style: none;
|
|
3842
3853
|
}
|
|
3843
3854
|
|
|
3844
3855
|
.reveal li {
|
|
3845
|
-
font-size:
|
|
3846
|
-
color: var(--
|
|
3847
|
-
line-height:
|
|
3848
|
-
margin-bottom:
|
|
3856
|
+
font-size: 22px;
|
|
3857
|
+
color: var(--text-secondary);
|
|
3858
|
+
line-height: 1.5;
|
|
3859
|
+
margin-bottom: 16px;
|
|
3860
|
+
padding-left: 28px;
|
|
3861
|
+
position: relative;
|
|
3849
3862
|
}
|
|
3850
3863
|
|
|
3851
|
-
.reveal li::
|
|
3852
|
-
|
|
3864
|
+
.reveal li::before {
|
|
3865
|
+
content: '';
|
|
3866
|
+
position: absolute;
|
|
3867
|
+
left: 0;
|
|
3868
|
+
top: 10px;
|
|
3869
|
+
width: 8px;
|
|
3870
|
+
height: 8px;
|
|
3871
|
+
background: var(--accent-blue);
|
|
3872
|
+
border-radius: 50%;
|
|
3853
3873
|
}
|
|
3854
3874
|
|
|
3855
3875
|
/* Slide Type Specific Styling */
|
|
3856
3876
|
|
|
3857
|
-
/* Title Slides */
|
|
3877
|
+
/* Title Slides - Bold and centered */
|
|
3858
3878
|
.slide-title, .slide-title_impact {
|
|
3859
3879
|
display: flex;
|
|
3860
3880
|
flex-direction: column;
|
|
3861
3881
|
justify-content: center;
|
|
3862
3882
|
align-items: center;
|
|
3863
3883
|
text-align: center;
|
|
3884
|
+
background: linear-gradient(135deg, var(--slide-bg-primary) 0%, #0a0a14 100%);
|
|
3864
3885
|
}
|
|
3865
3886
|
|
|
3866
3887
|
.slide-title h1, .slide-title_impact h1 {
|
|
3867
|
-
font-size:
|
|
3868
|
-
margin-bottom:
|
|
3888
|
+
font-size: 72px;
|
|
3889
|
+
margin-bottom: 16px;
|
|
3869
3890
|
}
|
|
3870
3891
|
|
|
3871
3892
|
.slide-title h2, .slide-title_impact h2 {
|
|
3872
|
-
font-size:
|
|
3873
|
-
|
|
3874
|
-
|
|
3893
|
+
font-size: 28px;
|
|
3894
|
+
font-weight: 400;
|
|
3895
|
+
color: var(--text-muted);
|
|
3875
3896
|
}
|
|
3876
3897
|
|
|
3877
|
-
/* Section Dividers -
|
|
3898
|
+
/* Section Dividers - Bold accent background */
|
|
3878
3899
|
.slide-section_divider {
|
|
3879
3900
|
display: flex;
|
|
3880
3901
|
flex-direction: column;
|
|
3881
3902
|
justify-content: center;
|
|
3882
3903
|
align-items: center;
|
|
3883
3904
|
text-align: center;
|
|
3884
|
-
background: linear-gradient(135deg,
|
|
3885
|
-
position: relative;
|
|
3905
|
+
background: linear-gradient(135deg, #0f3460 0%, #1a1a2e 100%);
|
|
3886
3906
|
}
|
|
3887
3907
|
|
|
3888
3908
|
.slide-section_divider h1 {
|
|
3889
|
-
color: #ffffff
|
|
3890
|
-
font-size:
|
|
3891
|
-
|
|
3909
|
+
color: #ffffff;
|
|
3910
|
+
font-size: 64px;
|
|
3911
|
+
font-weight: 700;
|
|
3892
3912
|
}
|
|
3893
3913
|
|
|
3894
|
-
/*
|
|
3914
|
+
/* Metrics Grid - McKinsey/BCG style cards */
|
|
3895
3915
|
.slide-big_number, .slide-metrics_grid {
|
|
3896
3916
|
text-align: center;
|
|
3897
3917
|
}
|
|
3898
3918
|
|
|
3919
|
+
.metrics-container {
|
|
3920
|
+
display: flex;
|
|
3921
|
+
justify-content: center;
|
|
3922
|
+
gap: 32px;
|
|
3923
|
+
flex-wrap: wrap;
|
|
3924
|
+
margin-top: 40px;
|
|
3925
|
+
}
|
|
3926
|
+
|
|
3899
3927
|
.metric {
|
|
3900
|
-
display:
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
border-radius:
|
|
3907
|
-
|
|
3908
|
-
box-shadow: var(--metric-card-shadow, var(--glass-shadow));
|
|
3928
|
+
display: flex;
|
|
3929
|
+
flex-direction: column;
|
|
3930
|
+
align-items: center;
|
|
3931
|
+
padding: 32px 48px;
|
|
3932
|
+
background: rgba(255, 255, 255, 0.05);
|
|
3933
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
3934
|
+
border-radius: 12px;
|
|
3935
|
+
min-width: 200px;
|
|
3909
3936
|
}
|
|
3910
3937
|
|
|
3911
3938
|
.metric-value {
|
|
3912
|
-
font-size:
|
|
3913
|
-
font-weight:
|
|
3914
|
-
color: var(--
|
|
3915
|
-
line-height:
|
|
3939
|
+
font-size: 48px;
|
|
3940
|
+
font-weight: 700;
|
|
3941
|
+
color: var(--accent-blue);
|
|
3942
|
+
line-height: 1.1;
|
|
3943
|
+
margin-bottom: 8px;
|
|
3916
3944
|
}
|
|
3917
3945
|
|
|
3918
3946
|
.metric-label {
|
|
3919
|
-
font-size:
|
|
3920
|
-
color: var(--
|
|
3921
|
-
|
|
3922
|
-
|
|
3947
|
+
font-size: 16px;
|
|
3948
|
+
color: var(--text-secondary);
|
|
3949
|
+
text-transform: uppercase;
|
|
3950
|
+
letter-spacing: 0.05em;
|
|
3923
3951
|
}
|
|
3924
3952
|
|
|
3925
|
-
.metric-trend-up {
|
|
3926
|
-
color: var(--
|
|
3953
|
+
.metric-trend-up .metric-value {
|
|
3954
|
+
color: var(--accent-green);
|
|
3927
3955
|
}
|
|
3928
3956
|
|
|
3929
|
-
.metric-trend-down {
|
|
3930
|
-
color: var(--
|
|
3957
|
+
.metric-trend-down .metric-value {
|
|
3958
|
+
color: var(--accent-orange);
|
|
3931
3959
|
}
|
|
3932
3960
|
|
|
3933
3961
|
/* Quote Slides - Using KB typography */
|
|
@@ -4225,9 +4253,15 @@ ${slides.map((slide, index) => this.renderSlide(slide, options.presentationType,
|
|
|
4225
4253
|
<div class="source">Source: ${this.renderMarkdown(data.source)}</div>`;
|
|
4226
4254
|
}
|
|
4227
4255
|
const bgImageUrl = this.getBackgroundImageUrl(slide.type, data, slideIndex, localImages, imageBasePath);
|
|
4228
|
-
|
|
4256
|
+
if (bgImageUrl) {
|
|
4257
|
+
return ` <section class="${slideClass}" data-background-image="${bgImageUrl}" data-background-size="cover" data-background-position="center" data-background-opacity="0.4">
|
|
4258
|
+
${content}
|
|
4259
|
+
</section>`;
|
|
4260
|
+
} else {
|
|
4261
|
+
return ` <section class="${slideClass}">
|
|
4229
4262
|
${content}
|
|
4230
4263
|
</section>`;
|
|
4264
|
+
}
|
|
4231
4265
|
}
|
|
4232
4266
|
renderTitleSlide(data) {
|
|
4233
4267
|
let html = "";
|
|
@@ -4432,7 +4466,13 @@ ${content}
|
|
|
4432
4466
|
}
|
|
4433
4467
|
/**
|
|
4434
4468
|
* Generate a background image URL based on slide type and content.
|
|
4435
|
-
* Priority: 1) slide.data.image, 2) local images array, 3)
|
|
4469
|
+
* Priority: 1) slide.data.image, 2) local images array, 3) NO RANDOM STOCK PHOTOS
|
|
4470
|
+
*
|
|
4471
|
+
* IMPORTANT: Random stock photos destroy credibility. A photo of flowers behind
|
|
4472
|
+
* a financial slide makes you look incompetent. Only use images that are:
|
|
4473
|
+
* - Explicitly provided by the user
|
|
4474
|
+
* - Actually relevant to the content
|
|
4475
|
+
* Otherwise, rely on clean gradients via CSS.
|
|
4436
4476
|
*/
|
|
4437
4477
|
getBackgroundImageUrl(slideType, slideData, slideIndex, localImages, imageBasePath) {
|
|
4438
4478
|
if (slideData.image) {
|
|
@@ -4445,7 +4485,7 @@ ${content}
|
|
|
4445
4485
|
return this.resolveImagePath(localImage, imageBasePath);
|
|
4446
4486
|
}
|
|
4447
4487
|
}
|
|
4448
|
-
return
|
|
4488
|
+
return "";
|
|
4449
4489
|
}
|
|
4450
4490
|
/**
|
|
4451
4491
|
* Resolve an image path - handles relative paths, absolute paths, and URLs.
|
package/dist/index.mjs
CHANGED
|
@@ -1432,6 +1432,12 @@ var SlideGenerator = class {
|
|
|
1432
1432
|
wordLimits;
|
|
1433
1433
|
bulletLimit;
|
|
1434
1434
|
allowedSlideTypes;
|
|
1435
|
+
// Track created slide titles to prevent duplicates
|
|
1436
|
+
createdSlideTitles = /* @__PURE__ */ new Set();
|
|
1437
|
+
// Track section dividers to avoid excessive blank slides
|
|
1438
|
+
sectionDividerCount = 0;
|
|
1439
|
+
maxSectionDividers = 4;
|
|
1440
|
+
// Only first 4 major sections get dividers
|
|
1435
1441
|
constructor() {
|
|
1436
1442
|
this.kb = getKB();
|
|
1437
1443
|
this.vds = getVisualDesignSystem();
|
|
@@ -1447,6 +1453,8 @@ var SlideGenerator = class {
|
|
|
1447
1453
|
*/
|
|
1448
1454
|
async generate(analysis, presentationType) {
|
|
1449
1455
|
await this.initialize();
|
|
1456
|
+
this.createdSlideTitles.clear();
|
|
1457
|
+
this.sectionDividerCount = 0;
|
|
1450
1458
|
this.loadRulesForType(presentationType);
|
|
1451
1459
|
const slides = [];
|
|
1452
1460
|
let slideIndex = 0;
|
|
@@ -1593,8 +1601,15 @@ var SlideGenerator = class {
|
|
|
1593
1601
|
*/
|
|
1594
1602
|
createSectionSlides(section, analysis, type, startIndex) {
|
|
1595
1603
|
const slides = [];
|
|
1604
|
+
const normalizedTitle = section.header.toLowerCase().trim();
|
|
1605
|
+
if (this.createdSlideTitles.has(normalizedTitle)) {
|
|
1606
|
+
return slides;
|
|
1607
|
+
}
|
|
1596
1608
|
const slideType = this.determineSlideType(section, type);
|
|
1597
|
-
|
|
1609
|
+
const hasSubstantiveContent = section.bullets.length > 0 || section.metrics.length > 0 || section.content.trim().length > 50;
|
|
1610
|
+
if (section.level === 2 && this.sectionDividerCount < this.maxSectionDividers && hasSubstantiveContent) {
|
|
1611
|
+
this.sectionDividerCount++;
|
|
1612
|
+
this.createdSlideTitles.add(normalizedTitle);
|
|
1598
1613
|
slides.push({
|
|
1599
1614
|
index: startIndex,
|
|
1600
1615
|
type: "section_divider",
|
|
@@ -1818,36 +1833,35 @@ var SlideGenerator = class {
|
|
|
1818
1833
|
return clean.trim();
|
|
1819
1834
|
}
|
|
1820
1835
|
/**
|
|
1821
|
-
* Clean bullets - strip markdown and
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1836
|
+
* Clean bullets - strip markdown and preserve COMPLETE, MEANINGFUL sentences.
|
|
1837
|
+
* Business presentations need full context. Never truncate mid-thought.
|
|
1838
|
+
* Reasonable limit: 20 words per bullet allows complete ideas.
|
|
1824
1839
|
*/
|
|
1825
1840
|
cleanBullets(bullets) {
|
|
1826
|
-
const maxBullets = Math.min(this.bulletLimit,
|
|
1827
|
-
const maxWordsPerBullet =
|
|
1841
|
+
const maxBullets = Math.min(this.bulletLimit, 6);
|
|
1842
|
+
const maxWordsPerBullet = 20;
|
|
1828
1843
|
return bullets.slice(0, maxBullets).map((b) => {
|
|
1829
1844
|
const clean = this.stripMarkdownSyntax(b);
|
|
1830
1845
|
const words = clean.split(/\s+/).filter((w) => w.length > 0);
|
|
1831
1846
|
if (words.length <= maxWordsPerBullet) {
|
|
1832
1847
|
return clean;
|
|
1833
1848
|
}
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
}
|
|
1849
|
+
const truncated = words.slice(0, maxWordsPerBullet).join(" ");
|
|
1850
|
+
const lastPeriod = truncated.lastIndexOf(".");
|
|
1851
|
+
const lastColon = truncated.lastIndexOf(":");
|
|
1852
|
+
const lastDash = truncated.lastIndexOf(" \u2013 ");
|
|
1853
|
+
const lastHyphen = truncated.lastIndexOf(" - ");
|
|
1854
|
+
const breakPoints = [lastPeriod, lastColon, lastDash, lastHyphen].filter((p) => p > 15);
|
|
1855
|
+
const bestBreak = Math.max(...breakPoints, -1);
|
|
1856
|
+
if (bestBreak > 15) {
|
|
1857
|
+
return truncated.slice(0, bestBreak + 1).trim();
|
|
1844
1858
|
}
|
|
1845
|
-
return
|
|
1859
|
+
return truncated;
|
|
1846
1860
|
}).filter((b) => b.length > 0);
|
|
1847
1861
|
}
|
|
1848
1862
|
/**
|
|
1849
1863
|
* Truncate text to word limit, stripping markdown first.
|
|
1850
|
-
*
|
|
1864
|
+
* Preserves complete sentences. Business content needs room to breathe.
|
|
1851
1865
|
*/
|
|
1852
1866
|
truncateToWordLimit(text) {
|
|
1853
1867
|
const cleanText = this.stripMarkdownSyntax(text);
|
|
@@ -1855,18 +1869,18 @@ var SlideGenerator = class {
|
|
|
1855
1869
|
return "";
|
|
1856
1870
|
}
|
|
1857
1871
|
const words = cleanText.split(/\s+/).filter((w) => w.length > 0);
|
|
1858
|
-
const
|
|
1859
|
-
if (words.length <=
|
|
1872
|
+
const bodyLimit = Math.min(this.wordLimits.max || 60, 50);
|
|
1873
|
+
if (words.length <= bodyLimit) {
|
|
1860
1874
|
return cleanText;
|
|
1861
1875
|
}
|
|
1862
|
-
const truncatedWords = words.slice(0,
|
|
1876
|
+
const truncatedWords = words.slice(0, bodyLimit);
|
|
1863
1877
|
const truncatedText = truncatedWords.join(" ");
|
|
1864
1878
|
const lastSentenceEnd = Math.max(
|
|
1865
1879
|
truncatedText.lastIndexOf("."),
|
|
1866
1880
|
truncatedText.lastIndexOf("!"),
|
|
1867
1881
|
truncatedText.lastIndexOf("?")
|
|
1868
1882
|
);
|
|
1869
|
-
if (lastSentenceEnd >
|
|
1883
|
+
if (lastSentenceEnd > 20) {
|
|
1870
1884
|
return truncatedText.slice(0, lastSentenceEnd + 1).trim();
|
|
1871
1885
|
}
|
|
1872
1886
|
return truncatedText;
|
|
@@ -1905,18 +1919,30 @@ var SlideGenerator = class {
|
|
|
1905
1919
|
}
|
|
1906
1920
|
/**
|
|
1907
1921
|
* Extract a label from context (nearby text around a data point).
|
|
1922
|
+
* CRITICAL: Strip all markdown table syntax first to avoid garbage labels.
|
|
1908
1923
|
*/
|
|
1909
1924
|
extractLabelFromContext(context) {
|
|
1910
|
-
|
|
1925
|
+
let cleanContext = context.replace(/\|/g, " ").replace(/-{2,}/g, "").replace(/:{1,2}/g, "").replace(/\s{2,}/g, " ").trim();
|
|
1926
|
+
if (!cleanContext || cleanContext.length < 3) {
|
|
1927
|
+
return "Value";
|
|
1928
|
+
}
|
|
1929
|
+
const beforeMatch = cleanContext.match(/^([A-Za-z][A-Za-z\s&]+?)(?:\s*[:=]?\s*)?[\d$%]/);
|
|
1911
1930
|
const beforeGroup = beforeMatch?.[1];
|
|
1912
|
-
if (beforeGroup && beforeGroup.trim().length <
|
|
1931
|
+
if (beforeGroup && beforeGroup.trim().length >= 3 && beforeGroup.trim().length < 40) {
|
|
1913
1932
|
return beforeGroup.trim();
|
|
1914
1933
|
}
|
|
1915
|
-
const afterMatch =
|
|
1934
|
+
const afterMatch = cleanContext.match(/[\d$%]+[KMB]?\s*(?:in|for|of|per)?\s*([A-Za-z][A-Za-z\s&]+)$/i);
|
|
1916
1935
|
const afterGroup = afterMatch?.[1];
|
|
1917
|
-
if (afterGroup && afterGroup.trim().length <
|
|
1936
|
+
if (afterGroup && afterGroup.trim().length >= 3 && afterGroup.trim().length < 40) {
|
|
1918
1937
|
return afterGroup.trim().replace(/[.,]$/, "");
|
|
1919
1938
|
}
|
|
1939
|
+
const words = cleanContext.match(/[A-Za-z]{3,}/g);
|
|
1940
|
+
if (words && words.length > 0) {
|
|
1941
|
+
const label = words.slice(0, 3).join(" ");
|
|
1942
|
+
if (label.length >= 3 && label.length < 40) {
|
|
1943
|
+
return label;
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1920
1946
|
return "Metric";
|
|
1921
1947
|
}
|
|
1922
1948
|
/**
|
|
@@ -3674,194 +3700,196 @@ ${slides.map((slide, index) => this.renderSlide(slide, options.presentationType,
|
|
|
3674
3700
|
/* CSS Variables from Knowledge Base - ALL styling driven by KB */
|
|
3675
3701
|
:root {
|
|
3676
3702
|
${cssVariables}
|
|
3703
|
+
/* Professional color scheme - McKinsey/BCG style */
|
|
3704
|
+
--slide-bg-primary: #1a1a2e;
|
|
3705
|
+
--slide-bg-secondary: #16213e;
|
|
3706
|
+
--slide-bg-accent: #0f3460;
|
|
3707
|
+
--text-primary: #ffffff;
|
|
3708
|
+
--text-secondary: rgba(255, 255, 255, 0.85);
|
|
3709
|
+
--text-muted: rgba(255, 255, 255, 0.6);
|
|
3710
|
+
--accent-blue: #4a9eff;
|
|
3711
|
+
--accent-green: #00d4aa;
|
|
3712
|
+
--accent-orange: #ff9f43;
|
|
3677
3713
|
}
|
|
3678
3714
|
|
|
3679
|
-
/* Premium Slide Styling -
|
|
3715
|
+
/* Premium Slide Styling - Clean, Professional */
|
|
3680
3716
|
.reveal {
|
|
3681
|
-
font-family: var(--font-body, system-ui, sans-serif);
|
|
3682
|
-
background: var(--color-background);
|
|
3717
|
+
font-family: var(--font-body, 'Inter', system-ui, sans-serif);
|
|
3683
3718
|
}
|
|
3684
3719
|
|
|
3685
3720
|
.reveal .slides {
|
|
3686
3721
|
text-align: left;
|
|
3687
3722
|
}
|
|
3688
3723
|
|
|
3724
|
+
/* All slides get a professional dark gradient background */
|
|
3689
3725
|
.reveal .slides section {
|
|
3690
|
-
padding:
|
|
3726
|
+
padding: 60px 80px !important;
|
|
3691
3727
|
box-sizing: border-box !important;
|
|
3692
|
-
|
|
3728
|
+
background: linear-gradient(135deg, var(--slide-bg-primary) 0%, var(--slide-bg-secondary) 50%, var(--slide-bg-accent) 100%);
|
|
3693
3729
|
}
|
|
3694
3730
|
|
|
3695
|
-
/*
|
|
3696
|
-
.reveal .slides section::before {
|
|
3731
|
+
/* When slide has a background image, add overlay for text readability */
|
|
3732
|
+
.reveal .slides section[data-background-image]::before {
|
|
3697
3733
|
content: '';
|
|
3698
3734
|
position: absolute;
|
|
3699
3735
|
top: 0;
|
|
3700
3736
|
left: 0;
|
|
3701
3737
|
right: 0;
|
|
3702
3738
|
bottom: 0;
|
|
3703
|
-
|
|
3704
|
-
background: radial-gradient(ellipse at center,
|
|
3705
|
-
rgba(0, 0, 0, 0.35) 0%,
|
|
3706
|
-
rgba(0, 0, 0, 0.55) 100%);
|
|
3739
|
+
background: rgba(0, 0, 0, 0.5);
|
|
3707
3740
|
z-index: 0;
|
|
3708
|
-
pointer-events: none;
|
|
3709
3741
|
}
|
|
3710
3742
|
|
|
3711
|
-
|
|
3712
|
-
.reveal .slides section > * {
|
|
3743
|
+
.reveal .slides section[data-background-image] > * {
|
|
3713
3744
|
position: relative;
|
|
3714
3745
|
z-index: 1;
|
|
3715
3746
|
}
|
|
3716
3747
|
|
|
3717
|
-
/*
|
|
3718
|
-
.reveal .slides section h1,
|
|
3719
|
-
.reveal .slides section h2,
|
|
3720
|
-
.reveal .slides section h3,
|
|
3721
|
-
.reveal .slides section p,
|
|
3722
|
-
.reveal .slides section li,
|
|
3723
|
-
.reveal .slides section .column,
|
|
3724
|
-
.reveal .slides section .key-message,
|
|
3725
|
-
.reveal .slides section .subtitle,
|
|
3726
|
-
.reveal .slides section ul,
|
|
3727
|
-
.reveal .slides section .metric-value,
|
|
3728
|
-
.reveal .slides section .metric-label {
|
|
3729
|
-
position: relative;
|
|
3730
|
-
z-index: 10;
|
|
3731
|
-
color: var(--color-text, #ffffff) !important;
|
|
3732
|
-
}
|
|
3733
|
-
|
|
3734
|
-
/* Typography Hierarchy - All from KB */
|
|
3735
|
-
/* Text shadow on ALL text for guaranteed readability over any background */
|
|
3736
|
-
.reveal h1, .reveal h2, .reveal h3, .reveal p, .reveal li, .reveal .key-message, .reveal .subtitle {
|
|
3737
|
-
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.9), 0 0 30px rgba(0, 0, 0, 0.7);
|
|
3738
|
-
}
|
|
3739
|
-
|
|
3748
|
+
/* Typography - Clean, readable, professional */
|
|
3740
3749
|
.reveal h1 {
|
|
3741
|
-
font-family: var(--font-title,
|
|
3742
|
-
font-size:
|
|
3743
|
-
font-weight:
|
|
3744
|
-
color: var(--
|
|
3745
|
-
margin-bottom:
|
|
3746
|
-
line-height:
|
|
3750
|
+
font-family: var(--font-title, 'Inter', system-ui, sans-serif);
|
|
3751
|
+
font-size: 56px;
|
|
3752
|
+
font-weight: 700;
|
|
3753
|
+
color: var(--text-primary);
|
|
3754
|
+
margin-bottom: 24px;
|
|
3755
|
+
line-height: 1.1;
|
|
3756
|
+
letter-spacing: -0.02em;
|
|
3747
3757
|
}
|
|
3748
3758
|
|
|
3749
3759
|
.reveal h2 {
|
|
3750
|
-
font-family: var(--font-title,
|
|
3751
|
-
font-size:
|
|
3752
|
-
font-weight:
|
|
3753
|
-
color: var(--
|
|
3754
|
-
|
|
3755
|
-
|
|
3760
|
+
font-family: var(--font-title, 'Inter', system-ui, sans-serif);
|
|
3761
|
+
font-size: 36px;
|
|
3762
|
+
font-weight: 600;
|
|
3763
|
+
color: var(--text-secondary);
|
|
3764
|
+
margin-bottom: 32px;
|
|
3765
|
+
line-height: 1.2;
|
|
3756
3766
|
}
|
|
3757
3767
|
|
|
3758
3768
|
.reveal h3 {
|
|
3759
|
-
font-
|
|
3760
|
-
font-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
margin-bottom: var(--spacing-md);
|
|
3769
|
+
font-size: 28px;
|
|
3770
|
+
font-weight: 600;
|
|
3771
|
+
color: var(--text-primary);
|
|
3772
|
+
margin-bottom: 16px;
|
|
3764
3773
|
}
|
|
3765
3774
|
|
|
3766
3775
|
.reveal p {
|
|
3767
|
-
font-size:
|
|
3768
|
-
color: var(--
|
|
3769
|
-
line-height:
|
|
3770
|
-
margin-bottom:
|
|
3776
|
+
font-size: 22px;
|
|
3777
|
+
color: var(--text-secondary);
|
|
3778
|
+
line-height: 1.6;
|
|
3779
|
+
margin-bottom: 24px;
|
|
3771
3780
|
}
|
|
3772
3781
|
|
|
3773
|
-
/* Bullet Points -
|
|
3782
|
+
/* Bullet Points - Professional styling */
|
|
3774
3783
|
.reveal ul, .reveal ol {
|
|
3775
|
-
margin-left:
|
|
3784
|
+
margin-left: 0;
|
|
3785
|
+
padding-left: 0;
|
|
3786
|
+
list-style: none;
|
|
3776
3787
|
}
|
|
3777
3788
|
|
|
3778
3789
|
.reveal li {
|
|
3779
|
-
font-size:
|
|
3780
|
-
color: var(--
|
|
3781
|
-
line-height:
|
|
3782
|
-
margin-bottom:
|
|
3790
|
+
font-size: 22px;
|
|
3791
|
+
color: var(--text-secondary);
|
|
3792
|
+
line-height: 1.5;
|
|
3793
|
+
margin-bottom: 16px;
|
|
3794
|
+
padding-left: 28px;
|
|
3795
|
+
position: relative;
|
|
3783
3796
|
}
|
|
3784
3797
|
|
|
3785
|
-
.reveal li::
|
|
3786
|
-
|
|
3798
|
+
.reveal li::before {
|
|
3799
|
+
content: '';
|
|
3800
|
+
position: absolute;
|
|
3801
|
+
left: 0;
|
|
3802
|
+
top: 10px;
|
|
3803
|
+
width: 8px;
|
|
3804
|
+
height: 8px;
|
|
3805
|
+
background: var(--accent-blue);
|
|
3806
|
+
border-radius: 50%;
|
|
3787
3807
|
}
|
|
3788
3808
|
|
|
3789
3809
|
/* Slide Type Specific Styling */
|
|
3790
3810
|
|
|
3791
|
-
/* Title Slides */
|
|
3811
|
+
/* Title Slides - Bold and centered */
|
|
3792
3812
|
.slide-title, .slide-title_impact {
|
|
3793
3813
|
display: flex;
|
|
3794
3814
|
flex-direction: column;
|
|
3795
3815
|
justify-content: center;
|
|
3796
3816
|
align-items: center;
|
|
3797
3817
|
text-align: center;
|
|
3818
|
+
background: linear-gradient(135deg, var(--slide-bg-primary) 0%, #0a0a14 100%);
|
|
3798
3819
|
}
|
|
3799
3820
|
|
|
3800
3821
|
.slide-title h1, .slide-title_impact h1 {
|
|
3801
|
-
font-size:
|
|
3802
|
-
margin-bottom:
|
|
3822
|
+
font-size: 72px;
|
|
3823
|
+
margin-bottom: 16px;
|
|
3803
3824
|
}
|
|
3804
3825
|
|
|
3805
3826
|
.slide-title h2, .slide-title_impact h2 {
|
|
3806
|
-
font-size:
|
|
3807
|
-
|
|
3808
|
-
|
|
3827
|
+
font-size: 28px;
|
|
3828
|
+
font-weight: 400;
|
|
3829
|
+
color: var(--text-muted);
|
|
3809
3830
|
}
|
|
3810
3831
|
|
|
3811
|
-
/* Section Dividers -
|
|
3832
|
+
/* Section Dividers - Bold accent background */
|
|
3812
3833
|
.slide-section_divider {
|
|
3813
3834
|
display: flex;
|
|
3814
3835
|
flex-direction: column;
|
|
3815
3836
|
justify-content: center;
|
|
3816
3837
|
align-items: center;
|
|
3817
3838
|
text-align: center;
|
|
3818
|
-
background: linear-gradient(135deg,
|
|
3819
|
-
position: relative;
|
|
3839
|
+
background: linear-gradient(135deg, #0f3460 0%, #1a1a2e 100%);
|
|
3820
3840
|
}
|
|
3821
3841
|
|
|
3822
3842
|
.slide-section_divider h1 {
|
|
3823
|
-
color: #ffffff
|
|
3824
|
-
font-size:
|
|
3825
|
-
|
|
3843
|
+
color: #ffffff;
|
|
3844
|
+
font-size: 64px;
|
|
3845
|
+
font-weight: 700;
|
|
3826
3846
|
}
|
|
3827
3847
|
|
|
3828
|
-
/*
|
|
3848
|
+
/* Metrics Grid - McKinsey/BCG style cards */
|
|
3829
3849
|
.slide-big_number, .slide-metrics_grid {
|
|
3830
3850
|
text-align: center;
|
|
3831
3851
|
}
|
|
3832
3852
|
|
|
3853
|
+
.metrics-container {
|
|
3854
|
+
display: flex;
|
|
3855
|
+
justify-content: center;
|
|
3856
|
+
gap: 32px;
|
|
3857
|
+
flex-wrap: wrap;
|
|
3858
|
+
margin-top: 40px;
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3833
3861
|
.metric {
|
|
3834
|
-
display:
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
border-radius:
|
|
3841
|
-
|
|
3842
|
-
box-shadow: var(--metric-card-shadow, var(--glass-shadow));
|
|
3862
|
+
display: flex;
|
|
3863
|
+
flex-direction: column;
|
|
3864
|
+
align-items: center;
|
|
3865
|
+
padding: 32px 48px;
|
|
3866
|
+
background: rgba(255, 255, 255, 0.05);
|
|
3867
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
3868
|
+
border-radius: 12px;
|
|
3869
|
+
min-width: 200px;
|
|
3843
3870
|
}
|
|
3844
3871
|
|
|
3845
3872
|
.metric-value {
|
|
3846
|
-
font-size:
|
|
3847
|
-
font-weight:
|
|
3848
|
-
color: var(--
|
|
3849
|
-
line-height:
|
|
3873
|
+
font-size: 48px;
|
|
3874
|
+
font-weight: 700;
|
|
3875
|
+
color: var(--accent-blue);
|
|
3876
|
+
line-height: 1.1;
|
|
3877
|
+
margin-bottom: 8px;
|
|
3850
3878
|
}
|
|
3851
3879
|
|
|
3852
3880
|
.metric-label {
|
|
3853
|
-
font-size:
|
|
3854
|
-
color: var(--
|
|
3855
|
-
|
|
3856
|
-
|
|
3881
|
+
font-size: 16px;
|
|
3882
|
+
color: var(--text-secondary);
|
|
3883
|
+
text-transform: uppercase;
|
|
3884
|
+
letter-spacing: 0.05em;
|
|
3857
3885
|
}
|
|
3858
3886
|
|
|
3859
|
-
.metric-trend-up {
|
|
3860
|
-
color: var(--
|
|
3887
|
+
.metric-trend-up .metric-value {
|
|
3888
|
+
color: var(--accent-green);
|
|
3861
3889
|
}
|
|
3862
3890
|
|
|
3863
|
-
.metric-trend-down {
|
|
3864
|
-
color: var(--
|
|
3891
|
+
.metric-trend-down .metric-value {
|
|
3892
|
+
color: var(--accent-orange);
|
|
3865
3893
|
}
|
|
3866
3894
|
|
|
3867
3895
|
/* Quote Slides - Using KB typography */
|
|
@@ -4159,9 +4187,15 @@ ${slides.map((slide, index) => this.renderSlide(slide, options.presentationType,
|
|
|
4159
4187
|
<div class="source">Source: ${this.renderMarkdown(data.source)}</div>`;
|
|
4160
4188
|
}
|
|
4161
4189
|
const bgImageUrl = this.getBackgroundImageUrl(slide.type, data, slideIndex, localImages, imageBasePath);
|
|
4162
|
-
|
|
4190
|
+
if (bgImageUrl) {
|
|
4191
|
+
return ` <section class="${slideClass}" data-background-image="${bgImageUrl}" data-background-size="cover" data-background-position="center" data-background-opacity="0.4">
|
|
4192
|
+
${content}
|
|
4193
|
+
</section>`;
|
|
4194
|
+
} else {
|
|
4195
|
+
return ` <section class="${slideClass}">
|
|
4163
4196
|
${content}
|
|
4164
4197
|
</section>`;
|
|
4198
|
+
}
|
|
4165
4199
|
}
|
|
4166
4200
|
renderTitleSlide(data) {
|
|
4167
4201
|
let html = "";
|
|
@@ -4366,7 +4400,13 @@ ${content}
|
|
|
4366
4400
|
}
|
|
4367
4401
|
/**
|
|
4368
4402
|
* Generate a background image URL based on slide type and content.
|
|
4369
|
-
* Priority: 1) slide.data.image, 2) local images array, 3)
|
|
4403
|
+
* Priority: 1) slide.data.image, 2) local images array, 3) NO RANDOM STOCK PHOTOS
|
|
4404
|
+
*
|
|
4405
|
+
* IMPORTANT: Random stock photos destroy credibility. A photo of flowers behind
|
|
4406
|
+
* a financial slide makes you look incompetent. Only use images that are:
|
|
4407
|
+
* - Explicitly provided by the user
|
|
4408
|
+
* - Actually relevant to the content
|
|
4409
|
+
* Otherwise, rely on clean gradients via CSS.
|
|
4370
4410
|
*/
|
|
4371
4411
|
getBackgroundImageUrl(slideType, slideData, slideIndex, localImages, imageBasePath) {
|
|
4372
4412
|
if (slideData.image) {
|
|
@@ -4379,7 +4419,7 @@ ${content}
|
|
|
4379
4419
|
return this.resolveImagePath(localImage, imageBasePath);
|
|
4380
4420
|
}
|
|
4381
4421
|
}
|
|
4382
|
-
return
|
|
4422
|
+
return "";
|
|
4383
4423
|
}
|
|
4384
4424
|
/**
|
|
4385
4425
|
* Resolve an image path - handles relative paths, absolute paths, and URLs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-presentation-master",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
24
|
"sync-knowledge": "bash scripts/sync-knowledge.sh",
|
|
25
|
-
"prepublishOnly": "npm run sync-knowledge && npm run build
|
|
25
|
+
"prepublishOnly": "npm run sync-knowledge && npm run build",
|
|
26
26
|
"qa": "node bin/cli.js validate",
|
|
27
27
|
"qa:strict": "node bin/cli.js validate --threshold 95"
|
|
28
28
|
},
|