hanseol-dev 5.0.3-dev.9 → 5.0.4-dev.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/agents/common/sub-agent.d.ts +2 -4
- package/dist/agents/common/sub-agent.d.ts.map +1 -1
- package/dist/agents/common/sub-agent.js +12 -11
- package/dist/agents/common/sub-agent.js.map +1 -1
- package/dist/agents/office/excel-agent.d.ts.map +1 -1
- package/dist/agents/office/excel-agent.js +1 -1
- package/dist/agents/office/excel-agent.js.map +1 -1
- package/dist/agents/office/excel-create-agent.d.ts.map +1 -1
- package/dist/agents/office/excel-create-agent.js +24 -4
- package/dist/agents/office/excel-create-agent.js.map +1 -1
- package/dist/agents/office/excel-create-prompts.d.ts +3 -3
- package/dist/agents/office/excel-create-prompts.d.ts.map +1 -1
- package/dist/agents/office/excel-create-prompts.js +83 -20
- package/dist/agents/office/excel-create-prompts.js.map +1 -1
- package/dist/agents/office/index.d.ts.map +1 -1
- package/dist/agents/office/index.js.map +1 -1
- package/dist/agents/office/powerpoint-agent.d.ts.map +1 -1
- package/dist/agents/office/powerpoint-agent.js +7 -1
- package/dist/agents/office/powerpoint-agent.js.map +1 -1
- package/dist/agents/office/powerpoint-create-agent.d.ts.map +1 -1
- package/dist/agents/office/powerpoint-create-agent.js +257 -281
- package/dist/agents/office/powerpoint-create-agent.js.map +1 -1
- package/dist/agents/office/powerpoint-create-prompts.d.ts +3 -2
- package/dist/agents/office/powerpoint-create-prompts.d.ts.map +1 -1
- package/dist/agents/office/powerpoint-create-prompts.js +284 -250
- package/dist/agents/office/powerpoint-create-prompts.js.map +1 -1
- package/dist/agents/office/prompts.d.ts +4 -4
- package/dist/agents/office/prompts.d.ts.map +1 -1
- package/dist/agents/office/prompts.js +2 -2
- package/dist/agents/office/word-agent.d.ts.map +1 -1
- package/dist/agents/office/word-agent.js +1 -1
- package/dist/agents/office/word-agent.js.map +1 -1
- package/dist/agents/office/word-create-agent.d.ts.map +1 -1
- package/dist/agents/office/word-create-agent.js +2 -4
- package/dist/agents/office/word-create-agent.js.map +1 -1
- package/dist/agents/office/word-create-prompts.d.ts +3 -3
- package/dist/agents/office/word-create-prompts.d.ts.map +1 -1
- package/dist/agents/office/word-create-prompts.js +167 -43
- package/dist/agents/office/word-create-prompts.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/prompts/agents/planning.d.ts.map +1 -1
- package/dist/prompts/agents/planning.js +7 -0
- package/dist/prompts/agents/planning.js.map +1 -1
- package/dist/tools/office/common/utils.d.ts +2 -0
- package/dist/tools/office/common/utils.d.ts.map +1 -1
- package/dist/tools/office/common/utils.js +4 -0
- package/dist/tools/office/common/utils.js.map +1 -1
- package/dist/tools/office/excel-client.d.ts +6 -0
- package/dist/tools/office/excel-client.d.ts.map +1 -1
- package/dist/tools/office/excel-client.js +77 -6
- package/dist/tools/office/excel-client.js.map +1 -1
- package/dist/tools/office/excel-tools/index.js +3 -3
- package/dist/tools/office/excel-tools/index.js.map +1 -1
- package/dist/tools/office/excel-tools/launch.d.ts.map +1 -1
- package/dist/tools/office/excel-tools/launch.js +3 -1
- package/dist/tools/office/excel-tools/launch.js.map +1 -1
- package/dist/tools/office/excel-tools/sheet-builders.d.ts +1 -0
- package/dist/tools/office/excel-tools/sheet-builders.d.ts.map +1 -1
- package/dist/tools/office/excel-tools/sheet-builders.js +79 -10
- package/dist/tools/office/excel-tools/sheet-builders.js.map +1 -1
- package/dist/tools/office/powerpoint-client.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-client.js +21 -18
- package/dist/tools/office/powerpoint-client.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/effects.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/effects.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/launch.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/launch.js +3 -1
- package/dist/tools/office/powerpoint-tools/launch.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/media.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/media.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/notes.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/notes.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/sections.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/sections.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/shapes.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/shapes.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/slides.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/slides.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/tables.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/tables.js.map +1 -1
- package/dist/tools/office/powerpoint-tools/text.d.ts.map +1 -1
- package/dist/tools/office/powerpoint-tools/text.js.map +1 -1
- package/dist/tools/office/word-client.d.ts +16 -0
- package/dist/tools/office/word-client.d.ts.map +1 -1
- package/dist/tools/office/word-client.js +295 -39
- package/dist/tools/office/word-client.js.map +1 -1
- package/dist/tools/office/word-tools/launch.d.ts.map +1 -1
- package/dist/tools/office/word-tools/launch.js +3 -1
- package/dist/tools/office/word-tools/launch.js.map +1 -1
- package/dist/tools/office/word-tools/section-builders.d.ts +2 -0
- package/dist/tools/office/word-tools/section-builders.d.ts.map +1 -1
- package/dist/tools/office/word-tools/section-builders.js +230 -50
- package/dist/tools/office/word-tools/section-builders.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { powerpointClient } from '../../tools/office/powerpoint-client.js';
|
|
|
4
4
|
import { getSubAgentPhaseLogger, getSubAgentToolCallLogger } from '../common/sub-agent.js';
|
|
5
5
|
import { logger } from '../../utils/logger.js';
|
|
6
6
|
import { getPlatform } from '../../utils/platform-utils.js';
|
|
7
|
-
import { PPT_DESIGN_PROMPT, validateSlideHtml,
|
|
7
|
+
import { PPT_DESIGN_PROMPT, validateSlideHtml, buildFreeHtmlPrompt, } from './powerpoint-create-prompts.js';
|
|
8
8
|
const DEFAULT_DESIGN = {
|
|
9
9
|
primary_color: '#1B2A4A',
|
|
10
10
|
accent_color: '#00D4AA',
|
|
@@ -246,32 +246,6 @@ function injectEdgeSizing(html, backgroundColor) {
|
|
|
246
246
|
}
|
|
247
247
|
return result;
|
|
248
248
|
}
|
|
249
|
-
function injectTitleContrastFix(html, designTextColor) {
|
|
250
|
-
const safeColor = designTextColor.replace(/'/g, "\\'");
|
|
251
|
-
const script = `<script>(function(){` +
|
|
252
|
-
`var dc='${safeColor}';` +
|
|
253
|
-
`function lum(c){var m=c.match(/[\\d.]+/g);if(!m||m.length<3)return -1;` +
|
|
254
|
-
`var s=[m[0]/255,m[1]/255,m[2]/255].map(function(v){return v<=0.03928?v/12.92:Math.pow((v+0.055)/1.055,2.4)});` +
|
|
255
|
-
`return 0.2126*s[0]+0.7152*s[1]+0.0722*s[2]}` +
|
|
256
|
-
`function getBg(el){while(el){var s=getComputedStyle(el);var bg=s.backgroundColor;` +
|
|
257
|
-
`var m=bg.match(/[\\d.]+/g);if(m&&m.length>=3){if(m.length<4||parseFloat(m[3])>0.1)return bg}` +
|
|
258
|
-
`el=el.parentElement}return'rgb(255,255,255)'}` +
|
|
259
|
-
`var els=document.querySelectorAll('h1,h2');` +
|
|
260
|
-
`for(var i=0;i<els.length;i++){var el=els[i];var cs=getComputedStyle(el);` +
|
|
261
|
-
`var tfc=cs.webkitTextFillColor||'';` +
|
|
262
|
-
`if(tfc==='transparent'||tfc==='rgba(0, 0, 0, 0)'){` +
|
|
263
|
-
`el.style.setProperty('-webkit-text-fill-color','initial','important')}` +
|
|
264
|
-
`if(parseFloat(cs.opacity)<0.6){el.style.setProperty('opacity','1','important')}` +
|
|
265
|
-
`var fg=cs.color;var bg=getBg(el);var fl=lum(fg),bl=lum(bg);` +
|
|
266
|
-
`if(fl>=0&&bl>=0){var r=(Math.max(fl,bl)+0.05)/(Math.min(fl,bl)+0.05);` +
|
|
267
|
-
`if(r<3){el.style.setProperty('color',dc,'important');` +
|
|
268
|
-
`el.style.setProperty('-webkit-text-fill-color',dc,'important')}}}` +
|
|
269
|
-
`})()<\/script>`;
|
|
270
|
-
if (html.includes('</body>')) {
|
|
271
|
-
return html.replace('</body>', `${script}</body>`);
|
|
272
|
-
}
|
|
273
|
-
return html + script;
|
|
274
|
-
}
|
|
275
249
|
function escapeHtml(text) {
|
|
276
250
|
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
277
251
|
}
|
|
@@ -429,139 +403,9 @@ body::after {
|
|
|
429
403
|
</body>
|
|
430
404
|
</html>`;
|
|
431
405
|
}
|
|
432
|
-
function isOverviewSlide(title, slideIndex) {
|
|
433
|
-
if (slideIndex !== 1)
|
|
434
|
-
return false;
|
|
435
|
-
return /개요|목차|overview|agenda|outline|순서|발표\s*구성|contents|목록/i.test(title);
|
|
436
|
-
}
|
|
437
|
-
function parseOverviewItems(contentDirection) {
|
|
438
|
-
const items = [];
|
|
439
|
-
const lines = contentDirection.split(/\n/).filter(l => l.trim());
|
|
440
|
-
for (const line of lines) {
|
|
441
|
-
const match = line.match(/^\d+[\.\)]\s*(.+?)(?:\s*[-–—:]\s*(.+))?$/);
|
|
442
|
-
if (match) {
|
|
443
|
-
items.push({ title: match[1].trim(), desc: match[2]?.trim() || '' });
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (items.length === 0) {
|
|
447
|
-
const parts = contentDirection.split(/[,;·•]/).map(s => s.trim()).filter(Boolean);
|
|
448
|
-
for (const part of parts) {
|
|
449
|
-
const sepMatch = part.match(/^(.+?)(?:\s*[-–—:]\s*(.+))?$/);
|
|
450
|
-
if (sepMatch) {
|
|
451
|
-
items.push({ title: sepMatch[1].trim(), desc: sepMatch[2]?.trim() || '' });
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
return items.slice(0, 5);
|
|
456
|
-
}
|
|
457
|
-
function buildOverviewSlideHtml(design, title, subtitle, items, slideNum) {
|
|
458
|
-
const badgeColors = [
|
|
459
|
-
design.primary_color, design.accent_color, design.gradient_end,
|
|
460
|
-
design.primary_color, design.accent_color,
|
|
461
|
-
];
|
|
462
|
-
const itemCount = items.length;
|
|
463
|
-
const topRow = itemCount <= 3 ? items : items.slice(0, Math.ceil(itemCount / 2));
|
|
464
|
-
const bottomRow = itemCount <= 3 ? [] : items.slice(Math.ceil(itemCount / 2));
|
|
465
|
-
function renderCard(item, idx) {
|
|
466
|
-
const color = badgeColors[idx % badgeColors.length];
|
|
467
|
-
return `
|
|
468
|
-
<div class="card">
|
|
469
|
-
<div class="badge" style="background:${color}">${idx + 1}</div>
|
|
470
|
-
<div class="card-title">${escapeHtml(item.title)}</div>
|
|
471
|
-
${item.desc ? `<div class="card-desc">${escapeHtml(item.desc)}</div>` : ''}
|
|
472
|
-
</div>`;
|
|
473
|
-
}
|
|
474
|
-
return `<!DOCTYPE html>
|
|
475
|
-
<html lang="ko">
|
|
476
|
-
<head>
|
|
477
|
-
<meta charset="UTF-8">
|
|
478
|
-
<style>
|
|
479
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
480
|
-
html, body { width: 1920px; height: 1080px; overflow: hidden; }
|
|
481
|
-
body {
|
|
482
|
-
background: ${design.background_color};
|
|
483
|
-
font-family: "${design.font_body}", "${design.font_title}", "Segoe UI", "Malgun Gothic", sans-serif;
|
|
484
|
-
display: flex; flex-direction: column;
|
|
485
|
-
word-break: keep-all; overflow-wrap: break-word;
|
|
486
|
-
}
|
|
487
|
-
.header {
|
|
488
|
-
background: linear-gradient(135deg, ${design.primary_color}, ${design.gradient_end});
|
|
489
|
-
padding: 48px 80px 40px;
|
|
490
|
-
flex-shrink: 0;
|
|
491
|
-
}
|
|
492
|
-
.header-title {
|
|
493
|
-
font-size: 52px; font-weight: 800; color: #ffffff;
|
|
494
|
-
font-family: "${design.font_title}", "Segoe UI", sans-serif;
|
|
495
|
-
margin-bottom: 8px;
|
|
496
|
-
}
|
|
497
|
-
.header-subtitle {
|
|
498
|
-
font-size: 24px; font-weight: 400; color: rgba(255,255,255,0.75);
|
|
499
|
-
}
|
|
500
|
-
.content {
|
|
501
|
-
flex: 1; display: flex; flex-direction: column;
|
|
502
|
-
padding: 48px 80px 40px;
|
|
503
|
-
gap: 24px;
|
|
504
|
-
justify-content: center;
|
|
505
|
-
}
|
|
506
|
-
.row {
|
|
507
|
-
display: flex; gap: 24px;
|
|
508
|
-
justify-content: center;
|
|
509
|
-
}
|
|
510
|
-
.card {
|
|
511
|
-
flex: 1;
|
|
512
|
-
max-width: 340px;
|
|
513
|
-
background: #ffffff;
|
|
514
|
-
border-radius: 16px;
|
|
515
|
-
padding: 36px 32px;
|
|
516
|
-
box-shadow: 0 4px 20px rgba(0,0,0,0.06);
|
|
517
|
-
display: flex; flex-direction: column;
|
|
518
|
-
align-items: center; text-align: center;
|
|
519
|
-
gap: 12px;
|
|
520
|
-
}
|
|
521
|
-
.badge {
|
|
522
|
-
width: 44px; height: 44px;
|
|
523
|
-
border-radius: 50%;
|
|
524
|
-
color: #ffffff;
|
|
525
|
-
font-size: 20px; font-weight: 700;
|
|
526
|
-
display: flex; align-items: center; justify-content: center;
|
|
527
|
-
flex-shrink: 0;
|
|
528
|
-
}
|
|
529
|
-
.card-title {
|
|
530
|
-
font-size: 26px; font-weight: 700; color: ${design.text_color};
|
|
531
|
-
line-height: 1.3;
|
|
532
|
-
}
|
|
533
|
-
.card-desc {
|
|
534
|
-
font-size: 22px; font-weight: 400; color: ${design.text_color}aa;
|
|
535
|
-
line-height: 1.5;
|
|
536
|
-
}
|
|
537
|
-
.page-num {
|
|
538
|
-
position: absolute;
|
|
539
|
-
bottom: 24px; right: 44px;
|
|
540
|
-
font-size: 13px;
|
|
541
|
-
color: ${design.text_color}55;
|
|
542
|
-
}
|
|
543
|
-
</style>
|
|
544
|
-
</head>
|
|
545
|
-
<body>
|
|
546
|
-
<div class="header">
|
|
547
|
-
<div class="header-title">${escapeHtml(title)}</div>
|
|
548
|
-
${subtitle ? `<div class="header-subtitle">${escapeHtml(subtitle)}</div>` : ''}
|
|
549
|
-
</div>
|
|
550
|
-
<div class="content">
|
|
551
|
-
<div class="row">
|
|
552
|
-
${topRow.map((item, idx) => renderCard(item, idx)).join('')}
|
|
553
|
-
</div>
|
|
554
|
-
${bottomRow.length > 0 ? `<div class="row">
|
|
555
|
-
${bottomRow.map((item, idx) => renderCard(item, topRow.length + idx)).join('')}
|
|
556
|
-
</div>` : ''}
|
|
557
|
-
</div>
|
|
558
|
-
<div class="page-num">${slideNum}</div>
|
|
559
|
-
</body>
|
|
560
|
-
</html>`;
|
|
561
|
-
}
|
|
562
406
|
function buildFallbackSlideHtml(design, title, contentDirection, slideNum) {
|
|
563
407
|
const items = [];
|
|
564
|
-
const parts = contentDirection.split(/\(\d+\)\s*|•\s*|\n-\s*|\n\d
|
|
408
|
+
const parts = contentDirection.split(/\(\d+\)\s*|•\s*|\n-\s*|\n\d+[.)]\s*|Step\s*\d+[.:]\s*/i);
|
|
565
409
|
for (const part of parts) {
|
|
566
410
|
const cleaned = part.replace(/Layout:.*$/i, '').trim();
|
|
567
411
|
if (cleaned.length > 5) {
|
|
@@ -569,8 +413,28 @@ function buildFallbackSlideHtml(design, title, contentDirection, slideNum) {
|
|
|
569
413
|
items.push(sentence.slice(0, 80));
|
|
570
414
|
}
|
|
571
415
|
}
|
|
416
|
+
if (items.length < 3) {
|
|
417
|
+
const commaParts = contentDirection.split(/[,;,;]\s*/);
|
|
418
|
+
for (const part of commaParts) {
|
|
419
|
+
const cleaned = part.replace(/Layout:.*$/i, '').trim();
|
|
420
|
+
if (cleaned.length > 8 && !items.includes(cleaned.slice(0, 80))) {
|
|
421
|
+
items.push(cleaned.slice(0, 80));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (items.length < 3) {
|
|
426
|
+
const sentences = contentDirection.split(/[.。!?]\s+/);
|
|
427
|
+
for (const s of sentences) {
|
|
428
|
+
const cleaned = s.replace(/Layout:.*$/i, '').trim();
|
|
429
|
+
if (cleaned.length > 10 && !items.some(i => i.startsWith(cleaned.slice(0, 20)))) {
|
|
430
|
+
items.push(cleaned.slice(0, 80));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
572
434
|
const bulletItems = items.slice(0, 6);
|
|
573
|
-
const
|
|
435
|
+
const useGrid = bulletItems.length >= 4;
|
|
436
|
+
const gridCols = useGrid ? 'grid-template-columns:1fr 1fr' : 'grid-template-columns:1fr';
|
|
437
|
+
const pointsHtml = bulletItems.map((item, i) => `<div class="point"><div class="point-num">${i + 1}</div><div class="point-text">${escapeHtml(item)}</div></div>`).join('\n ');
|
|
574
438
|
return `<!DOCTYPE html>
|
|
575
439
|
<html lang="ko">
|
|
576
440
|
<head>
|
|
@@ -589,9 +453,10 @@ body {
|
|
|
589
453
|
.slide-num { font-size: 14px; color: ${design.accent_color}; font-weight: 600; letter-spacing: 2px; text-transform: uppercase; margin-bottom: 12px; }
|
|
590
454
|
h1 { font-size: 52px; font-weight: 700; color: ${design.text_color}; font-family: "${design.font_title}", "Segoe UI", sans-serif; line-height: 1.2; }
|
|
591
455
|
.accent-bar { width: 80px; height: 4px; background: ${design.accent_color}; margin-top: 20px; border-radius: 2px; }
|
|
592
|
-
.content { flex: 1; display:
|
|
593
|
-
.
|
|
594
|
-
.
|
|
456
|
+
.content { flex: 1; display: grid; ${gridCols}; gap: 24px; align-content: center; }
|
|
457
|
+
.point { display: flex; align-items: flex-start; gap: 20px; padding: 28px 32px; background: #fff; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.06); }
|
|
458
|
+
.point-num { width: 48px; height: 48px; border-radius: 50%; background: ${design.accent_color}; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: 700; flex-shrink: 0; }
|
|
459
|
+
.point-text { font-size: 26px; line-height: 1.5; color: ${design.text_color}; flex: 1; }
|
|
595
460
|
</style>
|
|
596
461
|
</head>
|
|
597
462
|
<body>
|
|
@@ -601,7 +466,7 @@ h1 { font-size: 52px; font-weight: 700; color: ${design.text_color}; font-family
|
|
|
601
466
|
<div class="accent-bar"></div>
|
|
602
467
|
</div>
|
|
603
468
|
<div class="content">
|
|
604
|
-
${
|
|
469
|
+
${pointsHtml}
|
|
605
470
|
</div>
|
|
606
471
|
</body>
|
|
607
472
|
</html>`;
|
|
@@ -700,43 +565,60 @@ async function runDesignPhase(llmClient, instruction, phaseLogger) {
|
|
|
700
565
|
}
|
|
701
566
|
return plan;
|
|
702
567
|
}
|
|
703
|
-
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
568
|
+
function extractHtmlFromResponse(raw) {
|
|
569
|
+
const trimmed = raw.trim();
|
|
570
|
+
if (/^<!DOCTYPE/i.test(trimmed) || /^<html/i.test(trimmed)) {
|
|
571
|
+
return trimmed;
|
|
572
|
+
}
|
|
573
|
+
const fenceMatch = trimmed.match(/```(?:html)?\s*\n?(<!DOCTYPE[\s\S]*?<\/html>)\s*\n?```/i);
|
|
574
|
+
if (fenceMatch)
|
|
575
|
+
return fenceMatch[1];
|
|
576
|
+
const htmlMatch = trimmed.match(/(<!DOCTYPE[\s\S]*<\/html>)/i);
|
|
577
|
+
if (htmlMatch)
|
|
578
|
+
return htmlMatch[1];
|
|
579
|
+
const htmlTagMatch = trimmed.match(/(<html[\s\S]*<\/html>)/i);
|
|
580
|
+
if (htmlTagMatch)
|
|
581
|
+
return '<!DOCTYPE html>\n' + htmlTagMatch[1];
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
async function generateSingleSlideHtml(llmClient, slide, design, slideIndex, totalSlides, language) {
|
|
585
|
+
const contentDirection = (slide.content_direction || '').trim();
|
|
586
|
+
logger.info(`Slide ${slideIndex + 1}: Generating free-form HTML for "${slide.title}"`);
|
|
587
|
+
const htmlPrompt = buildFreeHtmlPrompt(slide.title, contentDirection, design, slideIndex, totalSlides, language);
|
|
708
588
|
try {
|
|
709
|
-
const
|
|
589
|
+
const res = await llmClient.chatCompletion({
|
|
710
590
|
messages: [
|
|
711
|
-
{ role: 'system', content:
|
|
712
|
-
{ role: 'user', content: 'Output the
|
|
591
|
+
{ role: 'system', content: htmlPrompt },
|
|
592
|
+
{ role: 'user', content: 'Output the complete HTML now.' },
|
|
713
593
|
],
|
|
714
|
-
temperature: 0.
|
|
715
|
-
max_tokens:
|
|
594
|
+
temperature: 0.6,
|
|
595
|
+
max_tokens: 4000,
|
|
716
596
|
});
|
|
717
|
-
const
|
|
718
|
-
const
|
|
719
|
-
let
|
|
720
|
-
if (!
|
|
721
|
-
|
|
722
|
-
messages: [
|
|
723
|
-
{ role: 'system', content: jsonPrompt },
|
|
724
|
-
{ role: 'user', content: 'Output ONLY valid JSON. No markdown fences, no explanation. Start with { and end with }.' },
|
|
725
|
-
],
|
|
726
|
-
temperature: 0.2,
|
|
727
|
-
max_tokens: 2000,
|
|
728
|
-
});
|
|
729
|
-
const retryMsg = retryRes.choices[0]?.message;
|
|
730
|
-
const retryRaw = retryMsg ? extractContent(retryMsg) : '';
|
|
731
|
-
slideData = parseContentFillJson(retryRaw, layoutType);
|
|
597
|
+
const msg = res.choices[0]?.message;
|
|
598
|
+
const rawHtml = msg ? extractContent(msg) : '';
|
|
599
|
+
let html = extractHtmlFromResponse(rawHtml);
|
|
600
|
+
if (html && !hasPlaceholderText(html)) {
|
|
601
|
+
return { html, isCodeTemplate: false };
|
|
732
602
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
603
|
+
logger.warn(`Slide ${slideIndex + 1}: First HTML attempt failed. Raw length: ${rawHtml.length}. Retrying...`);
|
|
604
|
+
const retryRes = await llmClient.chatCompletion({
|
|
605
|
+
messages: [
|
|
606
|
+
{ role: 'system', content: htmlPrompt },
|
|
607
|
+
{ role: 'user', content: 'Output ONLY the complete HTML document. Start with <!DOCTYPE html> and end with </html>. No explanation.' },
|
|
608
|
+
],
|
|
609
|
+
temperature: 0.4,
|
|
610
|
+
max_tokens: 4000,
|
|
611
|
+
});
|
|
612
|
+
const retryMsg = retryRes.choices[0]?.message;
|
|
613
|
+
const retryRaw = retryMsg ? extractContent(retryMsg) : '';
|
|
614
|
+
html = extractHtmlFromResponse(retryRaw);
|
|
615
|
+
if (html && !hasPlaceholderText(html)) {
|
|
616
|
+
return { html, isCodeTemplate: false };
|
|
736
617
|
}
|
|
618
|
+
logger.warn(`Slide ${slideIndex + 1}: Both HTML attempts failed.`);
|
|
737
619
|
}
|
|
738
620
|
catch (e) {
|
|
739
|
-
logger.warn(`Slide ${slideIndex + 1}:
|
|
621
|
+
logger.warn(`Slide ${slideIndex + 1}: HTML generation error: ${e}`);
|
|
740
622
|
}
|
|
741
623
|
return null;
|
|
742
624
|
}
|
|
@@ -759,18 +641,6 @@ async function generateAllHtml(llmClient, plan, companyName, titleSubtitle, kstD
|
|
|
759
641
|
isCodeTemplate: true,
|
|
760
642
|
});
|
|
761
643
|
}
|
|
762
|
-
else if (isOverviewSlide(slide.title, i)) {
|
|
763
|
-
const overviewItems = parseOverviewItems(slide.content_direction || '');
|
|
764
|
-
if (overviewItems.length >= 2) {
|
|
765
|
-
const firstLine = (slide.content_direction || '').split('\n')[0] || '';
|
|
766
|
-
const overviewSubtitle = /^\d/.test(firstLine.trim()) ? '' : firstLine.trim();
|
|
767
|
-
results.set(i, {
|
|
768
|
-
index: i,
|
|
769
|
-
html: buildOverviewSlideHtml(plan.design, slide.title, overviewSubtitle, overviewItems, i + 1),
|
|
770
|
-
isCodeTemplate: true,
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
644
|
}
|
|
775
645
|
const contentIndices = plan.slides
|
|
776
646
|
.map((s, i) => ({ slide: s, index: i }))
|
|
@@ -809,8 +679,7 @@ async function validateAndRegenerate(llmClient, htmlResults, plan, language, pha
|
|
|
809
679
|
const slide = plan.slides[index];
|
|
810
680
|
if (slide.type === 'title' || slide.type === 'closing')
|
|
811
681
|
continue;
|
|
812
|
-
const
|
|
813
|
-
const validation = validateSlideHtml(result.html, layoutType);
|
|
682
|
+
const validation = validateSlideHtml(result.html);
|
|
814
683
|
if (!validation.pass) {
|
|
815
684
|
logger.info(`Slide ${index + 1}: Post-validation failed: ${validation.feedback}`);
|
|
816
685
|
failedIndices.push(index);
|
|
@@ -852,23 +721,28 @@ async function validateAndRegenerate(llmClient, htmlResults, plan, language, pha
|
|
|
852
721
|
}
|
|
853
722
|
return htmlResults;
|
|
854
723
|
}
|
|
855
|
-
|
|
724
|
+
const MAX_SCREENSHOT_CONCURRENT = 4;
|
|
725
|
+
async function assemblePresentation(htmlResults, plan, timestamp, savePath, companyName, language, phaseLogger, toolCallLogger) {
|
|
856
726
|
const { writePath: tempWritePath, winPath: tempWinPath } = getTempDir();
|
|
857
727
|
ensureTempDir(tempWritePath);
|
|
728
|
+
const totalSlides = htmlResults.size;
|
|
729
|
+
const sortedEntries = [...htmlResults.entries()].sort((a, b) => a[0] - b[0]);
|
|
858
730
|
const builtSlides = [];
|
|
859
|
-
let failCount = 0;
|
|
860
731
|
let totalToolCalls = 0;
|
|
861
732
|
const tempFiles = [];
|
|
862
|
-
const
|
|
733
|
+
const initCountRes = await powerpointClient.powerpointGetSlideCount();
|
|
734
|
+
const initSlideCount = initCountRes['slide_count'] || 1;
|
|
735
|
+
const slidesToAdd = Math.max(0, totalSlides - initSlideCount);
|
|
736
|
+
if (phaseLogger)
|
|
737
|
+
phaseLogger('powerpoint-create', 'assembly', `Pre-creating ${totalSlides} slides (existing: ${initSlideCount}, adding: ${slidesToAdd})...`);
|
|
738
|
+
for (let i = 0; i < slidesToAdd; i++) {
|
|
739
|
+
await powerpointClient.powerpointAddSlide(7);
|
|
740
|
+
totalToolCalls++;
|
|
741
|
+
}
|
|
742
|
+
const slideFiles = new Map();
|
|
743
|
+
let slideNum = 0;
|
|
863
744
|
for (const [index, result] of sortedEntries) {
|
|
864
|
-
|
|
865
|
-
const slideNum = builtSlides.length + 1;
|
|
866
|
-
if (failCount >= 3) {
|
|
867
|
-
logger.warn('Too many slide failures, stopping');
|
|
868
|
-
break;
|
|
869
|
-
}
|
|
870
|
-
if (phaseLogger)
|
|
871
|
-
phaseLogger('powerpoint-create', 'assembly', `Rendering slide ${slideNum}: ${slidePlan.title}`);
|
|
745
|
+
slideNum++;
|
|
872
746
|
const htmlFileName = `hanseol_slide_${slideNum}_${timestamp}.html`;
|
|
873
747
|
const pngFileName = `hanseol_slide_${slideNum}_${timestamp}.png`;
|
|
874
748
|
const htmlWritePath = path.join(tempWritePath, htmlFileName);
|
|
@@ -877,98 +751,200 @@ async function assemblePresentation(htmlResults, plan, timestamp, savePath, phas
|
|
|
877
751
|
const pngWinPath = `${tempWinPath}\\${pngFileName}`;
|
|
878
752
|
try {
|
|
879
753
|
const viewportHtml = injectEdgeSizing(result.html, plan.design.background_color);
|
|
880
|
-
|
|
881
|
-
fs.writeFileSync(htmlWritePath, processed, 'utf-8');
|
|
754
|
+
fs.writeFileSync(htmlWritePath, viewportHtml, 'utf-8');
|
|
882
755
|
tempFiles.push(htmlWritePath);
|
|
756
|
+
slideFiles.set(index, { slideNum, htmlWritePath, pngWritePath, htmlWinPath, pngWinPath });
|
|
883
757
|
}
|
|
884
758
|
catch (e) {
|
|
885
|
-
logger.warn(`Slide ${slideNum}: Failed to write HTML
|
|
886
|
-
failCount++;
|
|
887
|
-
continue;
|
|
888
|
-
}
|
|
889
|
-
let renderSuccess = false;
|
|
890
|
-
try {
|
|
891
|
-
const renderResult = await powerpointClient.renderHtmlToImage(htmlWinPath, pngWinPath);
|
|
892
|
-
totalToolCalls++;
|
|
893
|
-
renderSuccess = renderResult.success;
|
|
894
|
-
if (!renderSuccess) {
|
|
895
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
896
|
-
const retryRender = await powerpointClient.renderHtmlToImage(htmlWinPath, pngWinPath);
|
|
897
|
-
totalToolCalls++;
|
|
898
|
-
renderSuccess = retryRender.success;
|
|
899
|
-
}
|
|
759
|
+
logger.warn(`Slide ${slideNum}: Failed to write HTML: ${e}`);
|
|
900
760
|
}
|
|
901
|
-
|
|
902
|
-
|
|
761
|
+
}
|
|
762
|
+
if (phaseLogger)
|
|
763
|
+
phaseLogger('powerpoint-create', 'assembly', `Rendering ${slideFiles.size} screenshots in parallel (batch ${MAX_SCREENSHOT_CONCURRENT})...`);
|
|
764
|
+
const screenshotEntries = [...slideFiles.entries()].sort((a, b) => a[0] - b[0]);
|
|
765
|
+
const screenshotSuccess = new Set();
|
|
766
|
+
for (let batch = 0; batch < screenshotEntries.length; batch += MAX_SCREENSHOT_CONCURRENT) {
|
|
767
|
+
const chunk = screenshotEntries.slice(batch, batch + MAX_SCREENSHOT_CONCURRENT);
|
|
768
|
+
const batchResults = await Promise.all(chunk.map(async ([index, files]) => {
|
|
769
|
+
let success = false;
|
|
903
770
|
try {
|
|
904
|
-
|
|
905
|
-
const retryRender = await powerpointClient.renderHtmlToImage(htmlWinPath, pngWinPath);
|
|
771
|
+
const result = await powerpointClient.renderHtmlToImage(files.htmlWinPath, files.pngWinPath);
|
|
906
772
|
totalToolCalls++;
|
|
907
|
-
|
|
773
|
+
success = result.success;
|
|
774
|
+
if (!success) {
|
|
775
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
776
|
+
const retry = await powerpointClient.renderHtmlToImage(files.htmlWinPath, files.pngWinPath);
|
|
777
|
+
totalToolCalls++;
|
|
778
|
+
success = retry.success;
|
|
779
|
+
}
|
|
908
780
|
}
|
|
909
781
|
catch {
|
|
910
|
-
|
|
782
|
+
try {
|
|
783
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
784
|
+
const retry = await powerpointClient.renderHtmlToImage(files.htmlWinPath, files.pngWinPath);
|
|
785
|
+
totalToolCalls++;
|
|
786
|
+
success = retry.success;
|
|
787
|
+
}
|
|
788
|
+
catch {
|
|
789
|
+
success = false;
|
|
790
|
+
}
|
|
911
791
|
}
|
|
792
|
+
try {
|
|
793
|
+
fs.unlinkSync(files.htmlWritePath);
|
|
794
|
+
}
|
|
795
|
+
catch { }
|
|
796
|
+
if (success) {
|
|
797
|
+
try {
|
|
798
|
+
const stat = fs.statSync(files.pngWritePath);
|
|
799
|
+
if (stat.size < 15000) {
|
|
800
|
+
logger.warn(`Slide ${files.slideNum}: Screenshot too small (${stat.size} bytes)`);
|
|
801
|
+
success = false;
|
|
802
|
+
try {
|
|
803
|
+
fs.unlinkSync(files.pngWritePath);
|
|
804
|
+
}
|
|
805
|
+
catch { }
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
catch { }
|
|
809
|
+
}
|
|
810
|
+
if (success)
|
|
811
|
+
tempFiles.push(files.pngWritePath);
|
|
812
|
+
return { index, success };
|
|
813
|
+
}));
|
|
814
|
+
for (const { index, success } of batchResults) {
|
|
815
|
+
if (success)
|
|
816
|
+
screenshotSuccess.add(index);
|
|
912
817
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
818
|
+
const done = Math.min(batch + MAX_SCREENSHOT_CONCURRENT, screenshotEntries.length);
|
|
819
|
+
if (phaseLogger)
|
|
820
|
+
phaseLogger('powerpoint-create', 'assembly', `Screenshots: ${done}/${screenshotEntries.length} done`);
|
|
821
|
+
}
|
|
822
|
+
if (phaseLogger)
|
|
823
|
+
phaseLogger('powerpoint-create', 'assembly', `Filling ${totalSlides} slides...`);
|
|
824
|
+
const unfilledSlideNums = [];
|
|
825
|
+
for (const [index] of sortedEntries) {
|
|
826
|
+
const files = slideFiles.get(index);
|
|
827
|
+
const slidePlan = plan.slides[index];
|
|
828
|
+
const htmlResult = htmlResults.get(index);
|
|
829
|
+
if (!files) {
|
|
830
|
+
const posInSorted = sortedEntries.findIndex(([idx]) => idx === index);
|
|
831
|
+
const inferredSlideNum = posInSorted >= 0 ? posInSorted + 1 : -1;
|
|
832
|
+
logger.warn(`Slide index ${index} (slideNum ${inferredSlideNum}): No file info, marking for deletion`);
|
|
833
|
+
if (inferredSlideNum > 0)
|
|
834
|
+
unfilledSlideNums.push(inferredSlideNum);
|
|
920
835
|
continue;
|
|
921
836
|
}
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
837
|
+
let filled = false;
|
|
838
|
+
if (screenshotSuccess.has(index)) {
|
|
839
|
+
const bgResult = await powerpointClient.powerpointAddFullSlideImage(files.slideNum, files.pngWinPath);
|
|
840
|
+
totalToolCalls++;
|
|
841
|
+
if (toolCallLogger)
|
|
842
|
+
toolCallLogger('powerpoint-create', 'addFullSlideImage', { slideNum: files.slideNum, imagePath: files.pngWinPath }, bgResult.success ? 'OK' : 'Failed', bgResult.success, files.slideNum, totalToolCalls);
|
|
843
|
+
filled = bgResult.success;
|
|
844
|
+
}
|
|
845
|
+
if (!filled) {
|
|
846
|
+
logger.info(`Slide ${files.slideNum}: Primary screenshot failed, trying fallback rendering...`);
|
|
847
|
+
const slide = plan.slides[index];
|
|
848
|
+
const fallbackHtml = buildFallbackSlideHtml(plan.design, slide.title, slide.content_direction || '', files.slideNum);
|
|
849
|
+
const fbHtmlName = `hanseol_fb_${files.slideNum}_${timestamp}.html`;
|
|
850
|
+
const fbPngName = `hanseol_fb_${files.slideNum}_${timestamp}.png`;
|
|
851
|
+
const fbHtmlWrite = path.join(tempWritePath, fbHtmlName);
|
|
852
|
+
const fbPngWrite = path.join(tempWritePath, fbPngName);
|
|
853
|
+
const fbHtmlWin = `${tempWinPath}\\${fbHtmlName}`;
|
|
854
|
+
const fbPngWin = `${tempWinPath}\\${fbPngName}`;
|
|
855
|
+
try {
|
|
856
|
+
const viewportHtml = injectEdgeSizing(fallbackHtml, plan.design.background_color);
|
|
857
|
+
fs.writeFileSync(fbHtmlWrite, viewportHtml, 'utf-8');
|
|
858
|
+
const fbResult = await powerpointClient.renderHtmlToImage(fbHtmlWin, fbPngWin);
|
|
859
|
+
totalToolCalls++;
|
|
927
860
|
try {
|
|
928
|
-
fs.unlinkSync(
|
|
861
|
+
fs.unlinkSync(fbHtmlWrite);
|
|
862
|
+
}
|
|
863
|
+
catch { }
|
|
864
|
+
if (fbResult.success) {
|
|
865
|
+
const bgResult = await powerpointClient.powerpointAddFullSlideImage(files.slideNum, fbPngWin);
|
|
866
|
+
totalToolCalls++;
|
|
867
|
+
filled = bgResult.success;
|
|
868
|
+
try {
|
|
869
|
+
fs.unlinkSync(fbPngWrite);
|
|
870
|
+
}
|
|
871
|
+
catch { }
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
catch (e) {
|
|
875
|
+
logger.warn(`Slide ${files.slideNum}: Fallback rendering also failed: ${e}`);
|
|
876
|
+
try {
|
|
877
|
+
fs.unlinkSync(fbHtmlWrite);
|
|
929
878
|
}
|
|
930
879
|
catch { }
|
|
931
|
-
continue;
|
|
932
880
|
}
|
|
933
881
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const addResult = await powerpointClient.powerpointAddSlide(7);
|
|
937
|
-
totalToolCalls++;
|
|
938
|
-
if (!addResult.success) {
|
|
939
|
-
logger.warn(`Slide ${slideNum}: Failed to add blank slide`);
|
|
940
|
-
failCount++;
|
|
941
|
-
continue;
|
|
942
|
-
}
|
|
943
|
-
const bgResult = await powerpointClient.powerpointAddFullSlideImage(slideNum, pngWinPath);
|
|
944
|
-
totalToolCalls++;
|
|
945
|
-
if (toolCallLogger)
|
|
946
|
-
toolCallLogger('powerpoint-create', 'addFullSlideImage', { slideNum, imagePath: pngWinPath }, bgResult.success ? 'OK' : 'Failed', bgResult.success, slideNum, totalToolCalls);
|
|
947
|
-
if (bgResult.success) {
|
|
948
|
-
builtSlides.push(`Slide ${slideNum}: ${slidePlan.title} (${slidePlan.type})`);
|
|
882
|
+
if (filled) {
|
|
883
|
+
builtSlides.push(`Slide ${files.slideNum}: ${slidePlan.title} (${slidePlan.type})`);
|
|
949
884
|
try {
|
|
950
|
-
await powerpointClient.powerpointAddNote(slideNum,
|
|
885
|
+
await powerpointClient.powerpointAddNote(files.slideNum, htmlResult.html);
|
|
951
886
|
}
|
|
952
887
|
catch { }
|
|
953
888
|
}
|
|
954
889
|
else {
|
|
955
|
-
logger.warn(`Slide ${slideNum}:
|
|
956
|
-
|
|
890
|
+
logger.warn(`Slide ${files.slideNum}: All rendering attempts failed, marking for deletion`);
|
|
891
|
+
unfilledSlideNums.push(files.slideNum);
|
|
957
892
|
}
|
|
958
893
|
}
|
|
959
|
-
if (
|
|
894
|
+
if (unfilledSlideNums.length > 0) {
|
|
895
|
+
const sortedDesc = [...unfilledSlideNums].sort((a, b) => b - a);
|
|
896
|
+
for (const slideNum of sortedDesc) {
|
|
897
|
+
try {
|
|
898
|
+
await powerpointClient.powerpointDeleteSlide(slideNum);
|
|
899
|
+
totalToolCalls++;
|
|
900
|
+
logger.info(`Deleted unfilled slide ${slideNum}`);
|
|
901
|
+
}
|
|
902
|
+
catch (e) {
|
|
903
|
+
logger.warn(`Failed to delete unfilled slide ${slideNum}: ${e}`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const hasClosingInPlan = plan.slides.some(s => s.type === 'closing');
|
|
908
|
+
const hasClosingBuilt = builtSlides.some(s => s.includes('(closing)'));
|
|
909
|
+
if (hasClosingInPlan && !hasClosingBuilt && builtSlides.length > 0) {
|
|
910
|
+
logger.info('Closing slide was lost during assembly — adding it now');
|
|
960
911
|
try {
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
912
|
+
await powerpointClient.powerpointAddSlide(7);
|
|
913
|
+
totalToolCalls++;
|
|
914
|
+
const slideCountRes = await powerpointClient.powerpointGetSlideCount();
|
|
915
|
+
const newSlideNum = slideCountRes['slide_count'] || builtSlides.length + 1;
|
|
916
|
+
const closingPlan = plan.slides.find(s => s.type === 'closing');
|
|
917
|
+
const closingTagline = (closingPlan.content_direction || '').replace(/감사합니다|thank\s*you/gi, '').trim() || undefined;
|
|
918
|
+
const closingHtml = buildClosingSlideHtml(plan.design, companyName, newSlideNum, language, closingTagline);
|
|
919
|
+
const clHtmlName = `hanseol_closing_${timestamp}.html`;
|
|
920
|
+
const clPngName = `hanseol_closing_${timestamp}.png`;
|
|
921
|
+
const clHtmlWrite = path.join(tempWritePath, clHtmlName);
|
|
922
|
+
const clPngWrite = path.join(tempWritePath, clPngName);
|
|
923
|
+
const clHtmlWin = `${tempWinPath}\\${clHtmlName}`;
|
|
924
|
+
const clPngWin = `${tempWinPath}\\${clPngName}`;
|
|
925
|
+
const viewportHtml = injectEdgeSizing(closingHtml, plan.design.background_color);
|
|
926
|
+
fs.writeFileSync(clHtmlWrite, viewportHtml, 'utf-8');
|
|
927
|
+
const ssResult = await powerpointClient.renderHtmlToImage(clHtmlWin, clPngWin);
|
|
928
|
+
totalToolCalls++;
|
|
929
|
+
try {
|
|
930
|
+
fs.unlinkSync(clHtmlWrite);
|
|
931
|
+
}
|
|
932
|
+
catch { }
|
|
933
|
+
if (ssResult.success) {
|
|
934
|
+
const bgResult = await powerpointClient.powerpointAddFullSlideImage(newSlideNum, clPngWin);
|
|
935
|
+
totalToolCalls++;
|
|
936
|
+
if (bgResult.success) {
|
|
937
|
+
builtSlides.push(`Slide ${newSlideNum}: ${closingPlan.title} (closing)`);
|
|
938
|
+
logger.info('Closing slide added successfully');
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
fs.unlinkSync(clPngWrite);
|
|
967
942
|
}
|
|
943
|
+
catch { }
|
|
968
944
|
}
|
|
969
945
|
}
|
|
970
946
|
catch (e) {
|
|
971
|
-
logger.warn(`Failed to
|
|
947
|
+
logger.warn(`Failed to add closing slide: ${e}`);
|
|
972
948
|
}
|
|
973
949
|
}
|
|
974
950
|
if (builtSlides.length > 0) {
|
|
@@ -1102,7 +1078,7 @@ async function runStructured(llmClient, instruction, explicitSavePath) {
|
|
|
1102
1078
|
const validatedResults = await validateAndRegenerate(llmClient, htmlResults, plan, language, phaseLogger);
|
|
1103
1079
|
if (phaseLogger)
|
|
1104
1080
|
phaseLogger('powerpoint-create', 'assembly', `Assembling ${validatedResults.size} slides into PowerPoint...`);
|
|
1105
|
-
const { builtSlides, totalToolCalls } = await assemblePresentation(validatedResults, plan, timestamp, savePath, phaseLogger, toolCallLogger);
|
|
1081
|
+
const { builtSlides, totalToolCalls } = await assemblePresentation(validatedResults, plan, timestamp, savePath, companyName, language, phaseLogger, toolCallLogger);
|
|
1106
1082
|
const duration = Date.now() - startTime;
|
|
1107
1083
|
const slideList = builtSlides.join('\n');
|
|
1108
1084
|
const summary = `Presentation COMPLETE — ${builtSlides.length} slides created and saved successfully.\nAll requested topics are covered across these slides. Do NOT add more slides or call powerpoint_modify_agent.\n\nSlides:\n${slideList}`;
|