claude-presentation-master 3.7.0 → 3.8.5

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.mjs CHANGED
@@ -2036,6 +2036,318 @@ async function initSlideGenerator() {
2036
2036
  return generator;
2037
2037
  }
2038
2038
 
2039
+ // src/core/SlideGeneratorV2.ts
2040
+ import { marked } from "marked";
2041
+ var MODE_CONFIGS = {
2042
+ // Consulting decks: 40-80 words per slide is NORMAL
2043
+ consulting_deck: {
2044
+ maxBulletsPerSlide: 7,
2045
+ maxWordsPerSlide: 100,
2046
+ // Not 20!
2047
+ minBulletsToKeep: 3
2048
+ },
2049
+ // Keynotes: fewer words, more impact
2050
+ keynote: {
2051
+ maxBulletsPerSlide: 4,
2052
+ maxWordsPerSlide: 30,
2053
+ minBulletsToKeep: 2
2054
+ },
2055
+ // Default: consulting style
2056
+ default: {
2057
+ maxBulletsPerSlide: 6,
2058
+ maxWordsPerSlide: 80,
2059
+ minBulletsToKeep: 3
2060
+ }
2061
+ };
2062
+ var SlideGeneratorV2 = class {
2063
+ config;
2064
+ presentationType;
2065
+ constructor(presentationType = "consulting_deck") {
2066
+ this.presentationType = presentationType;
2067
+ this.config = MODE_CONFIGS[presentationType] || MODE_CONFIGS.default;
2068
+ }
2069
+ /**
2070
+ * Generate slides from markdown content.
2071
+ * Simple, correct, no garbage.
2072
+ */
2073
+ generate(markdown, title) {
2074
+ const slides = [];
2075
+ let slideIndex = 0;
2076
+ const tokens = marked.lexer(markdown);
2077
+ const detectedTitle = title || this.extractTitle(tokens);
2078
+ slides.push({
2079
+ index: slideIndex++,
2080
+ type: "title",
2081
+ title: detectedTitle,
2082
+ content: {}
2083
+ });
2084
+ const sections = this.splitIntoSections(tokens);
2085
+ for (const section of sections) {
2086
+ const sectionSlides = this.processSectionContent(section, slideIndex);
2087
+ for (const slide of sectionSlides) {
2088
+ slide.index = slideIndex++;
2089
+ slides.push(slide);
2090
+ }
2091
+ }
2092
+ const lastSlide = slides[slides.length - 1];
2093
+ if (lastSlide && lastSlide.type !== "thank_you") {
2094
+ slides.push({
2095
+ index: slideIndex++,
2096
+ type: "thank_you",
2097
+ title: "Thank You",
2098
+ content: { subtext: "Questions?" }
2099
+ });
2100
+ }
2101
+ this.validateSlides(slides);
2102
+ return slides;
2103
+ }
2104
+ /**
2105
+ * Extract title from first H1 token.
2106
+ */
2107
+ extractTitle(tokens) {
2108
+ for (const token of tokens) {
2109
+ if (token.type === "heading" && token.depth === 1) {
2110
+ return token.text;
2111
+ }
2112
+ }
2113
+ return "Presentation";
2114
+ }
2115
+ /**
2116
+ * Split tokens into sections based on H2 headers.
2117
+ */
2118
+ splitIntoSections(tokens) {
2119
+ const sections = [];
2120
+ let currentSection = null;
2121
+ for (const token of tokens) {
2122
+ if (token.type === "heading" && token.depth === 2) {
2123
+ if (currentSection) {
2124
+ sections.push(currentSection);
2125
+ }
2126
+ currentSection = {
2127
+ title: token.text,
2128
+ tokens: []
2129
+ };
2130
+ } else if (token.type === "heading" && token.depth === 1) {
2131
+ continue;
2132
+ } else if (currentSection) {
2133
+ currentSection.tokens.push(token);
2134
+ }
2135
+ }
2136
+ if (currentSection && currentSection.tokens.length > 0) {
2137
+ sections.push(currentSection);
2138
+ }
2139
+ return sections;
2140
+ }
2141
+ /**
2142
+ * Process section content into slides.
2143
+ * Tables become table slides. Lists become bullet slides.
2144
+ */
2145
+ processSectionContent(section, startIndex) {
2146
+ const slides = [];
2147
+ const hasTable = section.tokens.some((t) => t.type === "table");
2148
+ const hasList = section.tokens.some((t) => t.type === "list");
2149
+ const hasSignificantText = this.hasSignificantParagraphs(section.tokens);
2150
+ if (hasTable) {
2151
+ const tableToken = section.tokens.find((t) => t.type === "table");
2152
+ if (tableToken) {
2153
+ slides.push(this.createTableSlide(section.title, tableToken));
2154
+ }
2155
+ }
2156
+ if (hasList) {
2157
+ const listSlides = this.createBulletSlides(section.title, section.tokens);
2158
+ slides.push(...listSlides);
2159
+ }
2160
+ if (hasSignificantText && !hasList && !hasTable) {
2161
+ slides.push(this.createContentSlide(section.title, section.tokens));
2162
+ }
2163
+ if (slides.length === 0 && section.tokens.length > 0) {
2164
+ const statement = this.extractStatement(section.tokens);
2165
+ if (statement) {
2166
+ slides.push({
2167
+ index: startIndex,
2168
+ type: "statement",
2169
+ title: section.title,
2170
+ content: { statement }
2171
+ });
2172
+ }
2173
+ }
2174
+ return slides;
2175
+ }
2176
+ /**
2177
+ * Create a table slide - render the table as-is, no extraction.
2178
+ */
2179
+ createTableSlide(title, tableToken) {
2180
+ const headers = tableToken.header.map((cell) => cell.text);
2181
+ const rows = tableToken.rows.map((row) => row.map((cell) => cell.text));
2182
+ return {
2183
+ index: 0,
2184
+ type: "table",
2185
+ title,
2186
+ content: {
2187
+ table: {
2188
+ headers,
2189
+ rows
2190
+ }
2191
+ }
2192
+ };
2193
+ }
2194
+ /**
2195
+ * Create bullet slides from list content.
2196
+ * NEVER truncate bullets. Split into multiple slides if needed.
2197
+ */
2198
+ createBulletSlides(title, tokens) {
2199
+ const slides = [];
2200
+ const allBullets = [];
2201
+ for (const token of tokens) {
2202
+ if (token.type === "list") {
2203
+ for (const item of token.items) {
2204
+ const bulletText = this.getCompleteItemText(item);
2205
+ if (bulletText.trim()) {
2206
+ allBullets.push(bulletText);
2207
+ }
2208
+ }
2209
+ }
2210
+ }
2211
+ const maxBullets = this.config.maxBulletsPerSlide;
2212
+ const minBullets = this.config.minBulletsToKeep;
2213
+ if (allBullets.length <= maxBullets) {
2214
+ if (allBullets.length >= minBullets || allBullets.length === 0) {
2215
+ slides.push({
2216
+ index: 0,
2217
+ type: "bullets",
2218
+ title,
2219
+ content: { bullets: allBullets }
2220
+ });
2221
+ } else {
2222
+ slides.push({
2223
+ index: 0,
2224
+ type: "bullets",
2225
+ title,
2226
+ content: { bullets: allBullets },
2227
+ notes: "Consider combining with adjacent content"
2228
+ });
2229
+ }
2230
+ } else {
2231
+ for (let i = 0; i < allBullets.length; i += maxBullets) {
2232
+ const chunk = allBullets.slice(i, i + maxBullets);
2233
+ const isFirst = i === 0;
2234
+ const slideTitle = isFirst ? title : `${title} (continued)`;
2235
+ slides.push({
2236
+ index: 0,
2237
+ type: "bullets",
2238
+ title: slideTitle,
2239
+ content: { bullets: chunk }
2240
+ });
2241
+ }
2242
+ }
2243
+ return slides;
2244
+ }
2245
+ /**
2246
+ * Get complete text from a list item, including nested content.
2247
+ * NEVER truncate.
2248
+ */
2249
+ getCompleteItemText(item) {
2250
+ let text = "";
2251
+ for (const token of item.tokens) {
2252
+ if (token.type === "text") {
2253
+ text += token.text;
2254
+ } else if (token.type === "paragraph") {
2255
+ const paraToken = token;
2256
+ text += paraToken.text;
2257
+ } else if (token.type === "strong") {
2258
+ text += token.text;
2259
+ } else if (token.type === "em") {
2260
+ text += token.text;
2261
+ }
2262
+ }
2263
+ return text.trim();
2264
+ }
2265
+ /**
2266
+ * Check if section has significant paragraph content.
2267
+ */
2268
+ hasSignificantParagraphs(tokens) {
2269
+ let wordCount = 0;
2270
+ for (const token of tokens) {
2271
+ if (token.type === "paragraph") {
2272
+ wordCount += token.text.split(/\s+/).length;
2273
+ }
2274
+ }
2275
+ return wordCount > 20;
2276
+ }
2277
+ /**
2278
+ * Create a content slide from paragraphs.
2279
+ */
2280
+ createContentSlide(title, tokens) {
2281
+ const paragraphs = [];
2282
+ for (const token of tokens) {
2283
+ if (token.type === "paragraph") {
2284
+ paragraphs.push(token.text);
2285
+ }
2286
+ }
2287
+ return {
2288
+ index: 0,
2289
+ type: "bullets",
2290
+ title,
2291
+ content: {
2292
+ body: paragraphs.join("\n\n")
2293
+ }
2294
+ };
2295
+ }
2296
+ /**
2297
+ * Extract a key statement from tokens.
2298
+ */
2299
+ extractStatement(tokens) {
2300
+ for (const token of tokens) {
2301
+ if (token.type === "paragraph" && token.text.length > 10) {
2302
+ return token.text;
2303
+ }
2304
+ }
2305
+ return null;
2306
+ }
2307
+ /**
2308
+ * VALIDATE ALL SLIDES - Fail loudly on garbage.
2309
+ */
2310
+ validateSlides(slides) {
2311
+ const errors = [];
2312
+ for (const slide of slides) {
2313
+ if (slide.content.bullets) {
2314
+ for (const bullet of slide.content.bullets) {
2315
+ if (bullet.endsWith("\u2192") || bullet.endsWith("...") || bullet.endsWith("-")) {
2316
+ errors.push(`Slide "${slide.title}": Truncated bullet detected: "${bullet}"`);
2317
+ }
2318
+ if (bullet.length < 5) {
2319
+ errors.push(`Slide "${slide.title}": Suspiciously short bullet: "${bullet}"`);
2320
+ }
2321
+ }
2322
+ }
2323
+ if (slide.content.metrics) {
2324
+ for (const metric of slide.content.metrics) {
2325
+ if (metric.label.length < 3) {
2326
+ errors.push(`Slide "${slide.title}": Garbage metric label: "${metric.label}"`);
2327
+ }
2328
+ if (metric.label.includes("|") || metric.label.includes("---")) {
2329
+ errors.push(`Slide "${slide.title}": Table syntax in label: "${metric.label}"`);
2330
+ }
2331
+ }
2332
+ }
2333
+ const hasContent = slide.content.bullets && slide.content.bullets.length > 0 || slide.content.table || slide.content.metrics || slide.content.statement || slide.content.body || slide.type === "title" || slide.type === "section" || slide.type === "thank_you";
2334
+ if (!hasContent) {
2335
+ errors.push(`Slide "${slide.title}": Empty slide with no content`);
2336
+ }
2337
+ }
2338
+ if (errors.length > 0) {
2339
+ console.warn("\n\u26A0\uFE0F QUALITY WARNINGS:");
2340
+ for (const error of errors) {
2341
+ console.warn(` - ${error}`);
2342
+ }
2343
+ console.warn("");
2344
+ }
2345
+ }
2346
+ };
2347
+ function createSlideGeneratorV2(type = "consulting_deck") {
2348
+ return new SlideGeneratorV2(type);
2349
+ }
2350
+
2039
2351
  // src/qa/SlideQualityReviewer.ts
2040
2352
  var ASSESSMENT_WEIGHTS = {
2041
2353
  visualImpact: 0.25,
@@ -4570,6 +4882,459 @@ async function initRenderer() {
4570
4882
  return renderer;
4571
4883
  }
4572
4884
 
4885
+ // src/output/RendererV2.ts
4886
+ var RendererV2 = class {
4887
+ /**
4888
+ * Render slides to complete HTML document.
4889
+ */
4890
+ render(slides, title = "Presentation") {
4891
+ const slidesHtml = slides.map((slide) => this.renderSlide(slide)).join("\n");
4892
+ return `<!DOCTYPE html>
4893
+ <html lang="en">
4894
+ <head>
4895
+ <meta charset="UTF-8">
4896
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4897
+ <title>${this.escapeHtml(title)}</title>
4898
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@4.5.0/dist/reveal.css">
4899
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@4.5.0/dist/theme/black.css">
4900
+ <style>
4901
+ ${this.getCSS()}
4902
+ </style>
4903
+ </head>
4904
+ <body>
4905
+ <div class="reveal">
4906
+ <div class="slides">
4907
+ ${slidesHtml}
4908
+ </div>
4909
+ </div>
4910
+ <script src="https://cdn.jsdelivr.net/npm/reveal.js@4.5.0/dist/reveal.js"></script>
4911
+ <script>
4912
+ Reveal.initialize({
4913
+ hash: true,
4914
+ transition: 'fade',
4915
+ transitionSpeed: 'default',
4916
+ center: false,
4917
+ width: 1920,
4918
+ height: 1080,
4919
+ margin: 0.04,
4920
+ });
4921
+ </script>
4922
+ </body>
4923
+ </html>`;
4924
+ }
4925
+ /**
4926
+ * Render a single slide.
4927
+ */
4928
+ renderSlide(slide) {
4929
+ const slideClass = `slide-${slide.type}`;
4930
+ let content = "";
4931
+ switch (slide.type) {
4932
+ case "title":
4933
+ content = this.renderTitleSlide(slide);
4934
+ break;
4935
+ case "section":
4936
+ content = this.renderSectionSlide(slide);
4937
+ break;
4938
+ case "bullets":
4939
+ content = this.renderBulletSlide(slide);
4940
+ break;
4941
+ case "table":
4942
+ content = this.renderTableSlide(slide);
4943
+ break;
4944
+ case "metrics":
4945
+ content = this.renderMetricsSlide(slide);
4946
+ break;
4947
+ case "statement":
4948
+ content = this.renderStatementSlide(slide);
4949
+ break;
4950
+ case "call_to_action":
4951
+ content = this.renderCTASlide(slide);
4952
+ break;
4953
+ case "thank_you":
4954
+ content = this.renderThankYouSlide(slide);
4955
+ break;
4956
+ default:
4957
+ content = this.renderBulletSlide(slide);
4958
+ }
4959
+ return ` <section class="${slideClass}">
4960
+ ${content}
4961
+ </section>`;
4962
+ }
4963
+ /**
4964
+ * Title slide - big, bold, centered.
4965
+ */
4966
+ renderTitleSlide(slide) {
4967
+ return ` <h1>${this.escapeHtml(slide.title)}</h1>
4968
+ ${slide.content.subtext ? `<h2>${this.escapeHtml(slide.content.subtext)}</h2>` : ""}`;
4969
+ }
4970
+ /**
4971
+ * Section divider - transition slide.
4972
+ */
4973
+ renderSectionSlide(slide) {
4974
+ return ` <h1>${this.escapeHtml(slide.title)}</h1>`;
4975
+ }
4976
+ /**
4977
+ * Bullet slide - the workhorse.
4978
+ */
4979
+ renderBulletSlide(slide) {
4980
+ let html = ` <h1>${this.escapeHtml(slide.title)}</h1>
4981
+ `;
4982
+ if (slide.content.body) {
4983
+ const paragraphs = slide.content.body.split("\n\n");
4984
+ for (const p of paragraphs) {
4985
+ html += ` <p>${this.escapeHtml(p)}</p>
4986
+ `;
4987
+ }
4988
+ }
4989
+ if (slide.content.bullets && slide.content.bullets.length > 0) {
4990
+ html += " <ul>\n";
4991
+ for (const bullet of slide.content.bullets) {
4992
+ const formattedBullet = this.formatBulletText(bullet);
4993
+ html += ` <li>${formattedBullet}</li>
4994
+ `;
4995
+ }
4996
+ html += " </ul>\n";
4997
+ }
4998
+ if (slide.content.source) {
4999
+ html += ` <div class="source">Source: ${this.escapeHtml(slide.content.source)}</div>
5000
+ `;
5001
+ }
5002
+ return html;
5003
+ }
5004
+ /**
5005
+ * Table slide - render tables properly.
5006
+ */
5007
+ renderTableSlide(slide) {
5008
+ let html = ` <h1>${this.escapeHtml(slide.title)}</h1>
5009
+ `;
5010
+ if (slide.content.table) {
5011
+ html += this.renderTable(slide.content.table);
5012
+ }
5013
+ return html;
5014
+ }
5015
+ /**
5016
+ * Render a table as clean HTML.
5017
+ */
5018
+ renderTable(table) {
5019
+ let html = ' <table class="data-table">\n';
5020
+ html += " <thead>\n <tr>\n";
5021
+ for (const header of table.headers) {
5022
+ html += ` <th>${this.escapeHtml(header)}</th>
5023
+ `;
5024
+ }
5025
+ html += " </tr>\n </thead>\n";
5026
+ html += " <tbody>\n";
5027
+ for (const row of table.rows) {
5028
+ html += " <tr>\n";
5029
+ for (let i = 0; i < row.length; i++) {
5030
+ const cell = row[i] || "";
5031
+ const isNumber = /^[\d$%,.\-+]+[KMB]?$/.test(cell.trim());
5032
+ const align = isNumber ? ' class="number"' : "";
5033
+ html += ` <td${align}>${this.escapeHtml(cell)}</td>
5034
+ `;
5035
+ }
5036
+ html += " </tr>\n";
5037
+ }
5038
+ html += " </tbody>\n";
5039
+ html += " </table>\n";
5040
+ return html;
5041
+ }
5042
+ /**
5043
+ * Metrics slide - big numbers with labels.
5044
+ */
5045
+ renderMetricsSlide(slide) {
5046
+ let html = ` <h1>${this.escapeHtml(slide.title)}</h1>
5047
+ `;
5048
+ html += ' <div class="metrics-container">\n';
5049
+ if (slide.content.metrics) {
5050
+ for (const metric of slide.content.metrics) {
5051
+ const trendClass = metric.trend ? ` metric-trend-${metric.trend}` : "";
5052
+ html += ` <div class="metric${trendClass}">
5053
+ <div class="metric-value">${this.escapeHtml(metric.value)}</div>
5054
+ <div class="metric-label">${this.escapeHtml(metric.label)}</div>
5055
+ </div>
5056
+ `;
5057
+ }
5058
+ }
5059
+ html += " </div>\n";
5060
+ return html;
5061
+ }
5062
+ /**
5063
+ * Statement slide - single powerful message.
5064
+ */
5065
+ renderStatementSlide(slide) {
5066
+ let html = ` <h1>${this.escapeHtml(slide.title)}</h1>
5067
+ `;
5068
+ if (slide.content.statement) {
5069
+ html += ` <p class="statement">${this.escapeHtml(slide.content.statement)}</p>
5070
+ `;
5071
+ }
5072
+ if (slide.content.subtext) {
5073
+ html += ` <p class="subtext">${this.escapeHtml(slide.content.subtext)}</p>
5074
+ `;
5075
+ }
5076
+ return html;
5077
+ }
5078
+ /**
5079
+ * Call to action slide.
5080
+ */
5081
+ renderCTASlide(slide) {
5082
+ let html = ` <h1>${this.escapeHtml(slide.title)}</h1>
5083
+ `;
5084
+ if (slide.content.bullets && slide.content.bullets.length > 0) {
5085
+ html += " <ul>\n";
5086
+ for (const bullet of slide.content.bullets) {
5087
+ html += ` <li>${this.formatBulletText(bullet)}</li>
5088
+ `;
5089
+ }
5090
+ html += " </ul>\n";
5091
+ }
5092
+ return html;
5093
+ }
5094
+ /**
5095
+ * Thank you slide.
5096
+ */
5097
+ renderThankYouSlide(slide) {
5098
+ return ` <h1>${this.escapeHtml(slide.title)}</h1>
5099
+ <h2>Questions?</h2>`;
5100
+ }
5101
+ /**
5102
+ * Format bullet text - handle bold, arrows, etc.
5103
+ */
5104
+ formatBulletText(text) {
5105
+ let formatted = this.escapeHtml(text);
5106
+ formatted = formatted.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
5107
+ formatted = formatted.replace(/\*(.+?)\*/g, "<em>$1</em>");
5108
+ formatted = formatted.replace(/→/g, " \u2192 ");
5109
+ formatted = formatted.replace(/->/g, " \u2192 ");
5110
+ return formatted;
5111
+ }
5112
+ /**
5113
+ * Escape HTML special characters.
5114
+ */
5115
+ escapeHtml(text) {
5116
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
5117
+ }
5118
+ /**
5119
+ * Professional CSS - McKinsey/BCG style.
5120
+ */
5121
+ getCSS() {
5122
+ return `
5123
+ /* Professional Dark Theme - No Random Images */
5124
+ :root {
5125
+ --bg-primary: #1a1a2e;
5126
+ --bg-secondary: #16213e;
5127
+ --bg-accent: #0f3460;
5128
+ --text-primary: #ffffff;
5129
+ --text-secondary: rgba(255, 255, 255, 0.85);
5130
+ --text-muted: rgba(255, 255, 255, 0.6);
5131
+ --accent-blue: #4a9eff;
5132
+ --accent-green: #00d4aa;
5133
+ --accent-orange: #ff9f43;
5134
+ --border-color: rgba(255, 255, 255, 0.1);
5135
+ }
5136
+
5137
+ .reveal {
5138
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
5139
+ }
5140
+
5141
+ .reveal .slides {
5142
+ text-align: left;
5143
+ }
5144
+
5145
+ .reveal .slides section {
5146
+ padding: 60px 80px;
5147
+ background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 50%, var(--bg-accent) 100%);
5148
+ height: 100%;
5149
+ box-sizing: border-box;
5150
+ }
5151
+
5152
+ /* Typography */
5153
+ .reveal h1 {
5154
+ font-size: 52px;
5155
+ font-weight: 700;
5156
+ color: var(--text-primary);
5157
+ margin-bottom: 32px;
5158
+ line-height: 1.1;
5159
+ letter-spacing: -0.02em;
5160
+ }
5161
+
5162
+ .reveal h2 {
5163
+ font-size: 32px;
5164
+ font-weight: 500;
5165
+ color: var(--text-secondary);
5166
+ margin-bottom: 24px;
5167
+ }
5168
+
5169
+ .reveal p {
5170
+ font-size: 24px;
5171
+ color: var(--text-secondary);
5172
+ line-height: 1.6;
5173
+ margin-bottom: 20px;
5174
+ }
5175
+
5176
+ /* Bullets - Complete, readable */
5177
+ .reveal ul {
5178
+ margin: 0;
5179
+ padding: 0;
5180
+ list-style: none;
5181
+ }
5182
+
5183
+ .reveal li {
5184
+ font-size: 24px;
5185
+ color: var(--text-secondary);
5186
+ line-height: 1.5;
5187
+ margin-bottom: 20px;
5188
+ padding-left: 32px;
5189
+ position: relative;
5190
+ }
5191
+
5192
+ .reveal li::before {
5193
+ content: '';
5194
+ position: absolute;
5195
+ left: 0;
5196
+ top: 12px;
5197
+ width: 10px;
5198
+ height: 10px;
5199
+ background: var(--accent-blue);
5200
+ border-radius: 50%;
5201
+ }
5202
+
5203
+ .reveal li strong {
5204
+ color: var(--text-primary);
5205
+ font-weight: 600;
5206
+ }
5207
+
5208
+ /* Tables - Clean, professional */
5209
+ .data-table {
5210
+ width: 100%;
5211
+ border-collapse: collapse;
5212
+ margin-top: 24px;
5213
+ font-size: 20px;
5214
+ }
5215
+
5216
+ .data-table th {
5217
+ background: rgba(255, 255, 255, 0.08);
5218
+ color: var(--text-primary);
5219
+ font-weight: 600;
5220
+ text-align: left;
5221
+ padding: 16px 20px;
5222
+ border-bottom: 2px solid var(--border-color);
5223
+ }
5224
+
5225
+ .data-table td {
5226
+ color: var(--text-secondary);
5227
+ padding: 14px 20px;
5228
+ border-bottom: 1px solid var(--border-color);
5229
+ }
5230
+
5231
+ .data-table td.number {
5232
+ text-align: right;
5233
+ font-family: 'SF Mono', 'Monaco', monospace;
5234
+ font-weight: 500;
5235
+ color: var(--accent-blue);
5236
+ }
5237
+
5238
+ .data-table tbody tr:hover td {
5239
+ background: rgba(255, 255, 255, 0.04);
5240
+ }
5241
+
5242
+ /* Metrics */
5243
+ .metrics-container {
5244
+ display: flex;
5245
+ justify-content: flex-start;
5246
+ gap: 40px;
5247
+ flex-wrap: wrap;
5248
+ margin-top: 40px;
5249
+ }
5250
+
5251
+ .metric {
5252
+ background: rgba(255, 255, 255, 0.05);
5253
+ border: 1px solid var(--border-color);
5254
+ border-radius: 12px;
5255
+ padding: 32px 48px;
5256
+ text-align: center;
5257
+ min-width: 200px;
5258
+ }
5259
+
5260
+ .metric-value {
5261
+ font-size: 56px;
5262
+ font-weight: 700;
5263
+ color: var(--accent-blue);
5264
+ line-height: 1;
5265
+ margin-bottom: 12px;
5266
+ }
5267
+
5268
+ .metric-trend-up .metric-value {
5269
+ color: var(--accent-green);
5270
+ }
5271
+
5272
+ .metric-trend-down .metric-value {
5273
+ color: var(--accent-orange);
5274
+ }
5275
+
5276
+ .metric-label {
5277
+ font-size: 16px;
5278
+ color: var(--text-muted);
5279
+ text-transform: uppercase;
5280
+ letter-spacing: 0.1em;
5281
+ }
5282
+
5283
+ /* Statement slides */
5284
+ .statement {
5285
+ font-size: 36px;
5286
+ color: var(--text-primary);
5287
+ line-height: 1.4;
5288
+ max-width: 80%;
5289
+ }
5290
+
5291
+ .subtext {
5292
+ font-size: 24px;
5293
+ color: var(--text-muted);
5294
+ }
5295
+
5296
+ /* Title slide */
5297
+ .slide-title {
5298
+ display: flex;
5299
+ flex-direction: column;
5300
+ justify-content: center;
5301
+ align-items: center;
5302
+ text-align: center;
5303
+ }
5304
+
5305
+ .slide-title h1 {
5306
+ font-size: 72px;
5307
+ }
5308
+
5309
+ /* Thank you slide */
5310
+ .slide-thank_you {
5311
+ display: flex;
5312
+ flex-direction: column;
5313
+ justify-content: center;
5314
+ align-items: center;
5315
+ text-align: center;
5316
+ }
5317
+
5318
+ .slide-thank_you h1 {
5319
+ font-size: 72px;
5320
+ margin-bottom: 16px;
5321
+ }
5322
+
5323
+ /* Source citation */
5324
+ .source {
5325
+ position: absolute;
5326
+ bottom: 24px;
5327
+ left: 80px;
5328
+ font-size: 14px;
5329
+ color: var(--text-muted);
5330
+ }
5331
+ `;
5332
+ }
5333
+ };
5334
+ function createRendererV2() {
5335
+ return new RendererV2();
5336
+ }
5337
+
4573
5338
  // src/image/NanoBananaProvider.ts
4574
5339
  var NanoBananaProvider = class {
4575
5340
  name = "NanoBanana Pro";
@@ -5612,11 +6377,15 @@ export {
5612
6377
  NanoBananaProvider,
5613
6378
  Remediator,
5614
6379
  Renderer,
6380
+ RendererV2,
5615
6381
  SlideGenerator,
6382
+ SlideGeneratorV2,
5616
6383
  SlideQualityReviewer,
5617
6384
  VERSION,
5618
6385
  VisualDesignSystem,
5619
6386
  createNanoBananaProvider,
6387
+ createRendererV2,
6388
+ createSlideGeneratorV2,
5620
6389
  generate,
5621
6390
  getContentAnalyzer,
5622
6391
  getDeckQualityReviewer,