hanseol-dev 5.0.2-dev.9 → 5.0.2-dev.91

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.
Files changed (122) hide show
  1. package/dist/agents/common/sub-agent.d.ts +21 -0
  2. package/dist/agents/common/sub-agent.d.ts.map +1 -1
  3. package/dist/agents/common/sub-agent.js +195 -10
  4. package/dist/agents/common/sub-agent.js.map +1 -1
  5. package/dist/agents/index.d.ts +1 -1
  6. package/dist/agents/index.d.ts.map +1 -1
  7. package/dist/agents/index.js +1 -1
  8. package/dist/agents/index.js.map +1 -1
  9. package/dist/agents/office/excel-agent.d.ts +1 -1
  10. package/dist/agents/office/excel-agent.d.ts.map +1 -1
  11. package/dist/agents/office/excel-agent.js +6 -6
  12. package/dist/agents/office/excel-agent.js.map +1 -1
  13. package/dist/agents/office/excel-create-agent.d.ts +3 -0
  14. package/dist/agents/office/excel-create-agent.d.ts.map +1 -0
  15. package/dist/agents/office/excel-create-agent.js +38 -0
  16. package/dist/agents/office/excel-create-agent.js.map +1 -0
  17. package/dist/agents/office/excel-create-prompts.d.ts +4 -0
  18. package/dist/agents/office/excel-create-prompts.d.ts.map +1 -0
  19. package/dist/agents/office/excel-create-prompts.js +154 -0
  20. package/dist/agents/office/excel-create-prompts.js.map +1 -0
  21. package/dist/agents/office/index.d.ts +6 -3
  22. package/dist/agents/office/index.d.ts.map +1 -1
  23. package/dist/agents/office/index.js +6 -3
  24. package/dist/agents/office/index.js.map +1 -1
  25. package/dist/agents/office/powerpoint-agent.d.ts +1 -1
  26. package/dist/agents/office/powerpoint-agent.d.ts.map +1 -1
  27. package/dist/agents/office/powerpoint-agent.js +6 -6
  28. package/dist/agents/office/powerpoint-agent.js.map +1 -1
  29. package/dist/agents/office/powerpoint-create-agent.d.ts +3 -0
  30. package/dist/agents/office/powerpoint-create-agent.d.ts.map +1 -0
  31. package/dist/agents/office/powerpoint-create-agent.js +1062 -0
  32. package/dist/agents/office/powerpoint-create-agent.js.map +1 -0
  33. package/dist/agents/office/powerpoint-create-prompts.d.ts +19 -0
  34. package/dist/agents/office/powerpoint-create-prompts.d.ts.map +1 -0
  35. package/dist/agents/office/powerpoint-create-prompts.js +389 -0
  36. package/dist/agents/office/powerpoint-create-prompts.js.map +1 -0
  37. package/dist/agents/office/prompts.d.ts +10 -3
  38. package/dist/agents/office/prompts.d.ts.map +1 -1
  39. package/dist/agents/office/prompts.js +451 -52
  40. package/dist/agents/office/prompts.js.map +1 -1
  41. package/dist/agents/office/word-agent.d.ts +1 -1
  42. package/dist/agents/office/word-agent.d.ts.map +1 -1
  43. package/dist/agents/office/word-agent.js +6 -6
  44. package/dist/agents/office/word-agent.js.map +1 -1
  45. package/dist/agents/office/word-create-agent.d.ts +3 -0
  46. package/dist/agents/office/word-create-agent.d.ts.map +1 -0
  47. package/dist/agents/office/word-create-agent.js +38 -0
  48. package/dist/agents/office/word-create-agent.js.map +1 -0
  49. package/dist/agents/office/word-create-prompts.d.ts +4 -0
  50. package/dist/agents/office/word-create-prompts.d.ts.map +1 -0
  51. package/dist/agents/office/word-create-prompts.js +164 -0
  52. package/dist/agents/office/word-create-prompts.js.map +1 -0
  53. package/dist/cli.js +3 -0
  54. package/dist/cli.js.map +1 -1
  55. package/dist/constants.d.ts +1 -1
  56. package/dist/constants.d.ts.map +1 -1
  57. package/dist/constants.js +1 -1
  58. package/dist/constants.js.map +1 -1
  59. package/dist/pipe/pipe-runner.d.ts.map +1 -1
  60. package/dist/pipe/pipe-runner.js +20 -0
  61. package/dist/pipe/pipe-runner.js.map +1 -1
  62. package/dist/prompts/agents/planning.d.ts.map +1 -1
  63. package/dist/prompts/agents/planning.js +43 -18
  64. package/dist/prompts/agents/planning.js.map +1 -1
  65. package/dist/prompts/shared/tool-usage.d.ts.map +1 -1
  66. package/dist/prompts/shared/tool-usage.js +6 -3
  67. package/dist/prompts/shared/tool-usage.js.map +1 -1
  68. package/dist/prompts/system/plan-execute.js +1 -1
  69. package/dist/tools/office/excel-client.js +4 -4
  70. package/dist/tools/office/excel-tools/index.d.ts +2 -0
  71. package/dist/tools/office/excel-tools/index.d.ts.map +1 -1
  72. package/dist/tools/office/excel-tools/index.js +12 -0
  73. package/dist/tools/office/excel-tools/index.js.map +1 -1
  74. package/dist/tools/office/excel-tools/sheet-builders.d.ts +8 -0
  75. package/dist/tools/office/excel-tools/sheet-builders.d.ts.map +1 -0
  76. package/dist/tools/office/excel-tools/sheet-builders.js +414 -0
  77. package/dist/tools/office/excel-tools/sheet-builders.js.map +1 -0
  78. package/dist/tools/office/excel-tools.d.ts +1 -1
  79. package/dist/tools/office/excel-tools.d.ts.map +1 -1
  80. package/dist/tools/office/excel-tools.js +1 -1
  81. package/dist/tools/office/excel-tools.js.map +1 -1
  82. package/dist/tools/office/powerpoint-client.d.ts +3 -0
  83. package/dist/tools/office/powerpoint-client.d.ts.map +1 -1
  84. package/dist/tools/office/powerpoint-client.js +136 -10
  85. package/dist/tools/office/powerpoint-client.js.map +1 -1
  86. package/dist/tools/office/powerpoint-tools/export.d.ts.map +1 -1
  87. package/dist/tools/office/powerpoint-tools/export.js +16 -1
  88. package/dist/tools/office/powerpoint-tools/export.js.map +1 -1
  89. package/dist/tools/office/powerpoint-tools/index.d.ts +2 -0
  90. package/dist/tools/office/powerpoint-tools/index.d.ts.map +1 -1
  91. package/dist/tools/office/powerpoint-tools/index.js +7 -0
  92. package/dist/tools/office/powerpoint-tools/index.js.map +1 -1
  93. package/dist/tools/office/powerpoint-tools/launch.d.ts.map +1 -1
  94. package/dist/tools/office/powerpoint-tools/launch.js +2 -0
  95. package/dist/tools/office/powerpoint-tools/launch.js.map +1 -1
  96. package/dist/tools/office/powerpoint-tools/layout-builders.d.ts +12 -0
  97. package/dist/tools/office/powerpoint-tools/layout-builders.d.ts.map +1 -0
  98. package/dist/tools/office/powerpoint-tools/layout-builders.js +785 -0
  99. package/dist/tools/office/powerpoint-tools/layout-builders.js.map +1 -0
  100. package/dist/tools/office/powerpoint-tools/slides.js +1 -1
  101. package/dist/tools/office/powerpoint-tools/slides.js.map +1 -1
  102. package/dist/tools/office/powerpoint-tools.d.ts +1 -1
  103. package/dist/tools/office/powerpoint-tools.d.ts.map +1 -1
  104. package/dist/tools/office/powerpoint-tools.js +1 -1
  105. package/dist/tools/office/powerpoint-tools.js.map +1 -1
  106. package/dist/tools/office/word-client.js +4 -4
  107. package/dist/tools/office/word-tools/index.d.ts +4 -1
  108. package/dist/tools/office/word-tools/index.d.ts.map +1 -1
  109. package/dist/tools/office/word-tools/index.js +15 -0
  110. package/dist/tools/office/word-tools/index.js.map +1 -1
  111. package/dist/tools/office/word-tools/section-builders.d.ts +10 -0
  112. package/dist/tools/office/word-tools/section-builders.d.ts.map +1 -0
  113. package/dist/tools/office/word-tools/section-builders.js +421 -0
  114. package/dist/tools/office/word-tools/section-builders.js.map +1 -0
  115. package/dist/tools/office/word-tools.d.ts +1 -1
  116. package/dist/tools/office/word-tools.d.ts.map +1 -1
  117. package/dist/tools/office/word-tools.js +1 -1
  118. package/dist/tools/office/word-tools.js.map +1 -1
  119. package/dist/tools/registry.d.ts.map +1 -1
  120. package/dist/tools/registry.js +7 -4
  121. package/dist/tools/registry.js.map +1 -1
  122. package/package.json +1 -1
@@ -0,0 +1,785 @@
1
+ import { powerpointClient } from '../powerpoint-client.js';
2
+ import { OFFICE_CATEGORIES } from '../common/constants.js';
3
+ const COLOR_PRESETS = {
4
+ MODERN_TECH: { primary: '#0D1B2A', accent: '#1B998B', light: '#E0F7F5', highlight: '#3CDFFF', sidebar: '#14514A' },
5
+ WARM_EXECUTIVE: { primary: '#2C1810', accent: '#C45B28', light: '#FFF3EC', highlight: '#E8A87C', sidebar: '#8B4513' },
6
+ CLEAN_MINIMAL: { primary: '#1A1A2E', accent: '#16213E', light: '#F5F5F5', highlight: '#0F3460', sidebar: '#2C3E6B' },
7
+ CORPORATE: { primary: '#1B3A5C', accent: '#2E5090', light: '#EBF0F7', highlight: '#B0C4DE', sidebar: '#1B3A5C' },
8
+ NATURE_FRESH: { primary: '#1B4332', accent: '#2D6A4F', light: '#D8F3DC', highlight: '#52B788', sidebar: '#1B4332' },
9
+ BOLD_MODERN: { primary: '#1A1A2E', accent: '#E63946', light: '#F8F9FA', highlight: '#FF6B6B', sidebar: '#2B2D42' },
10
+ };
11
+ const FONT_PRESETS = {
12
+ MODERN_TECH: { title: 'Segoe UI', body: '맑은 고딕' },
13
+ WARM_EXECUTIVE: { title: 'Georgia', body: '맑은 고딕' },
14
+ CLEAN_MINIMAL: { title: '맑은 고딕', body: '돋움' },
15
+ CORPORATE: { title: 'Calibri', body: '맑은 고딕' },
16
+ NATURE_FRESH: { title: '굴림', body: '맑은 고딕' },
17
+ BOLD_MODERN: { title: 'Arial Black', body: '맑은 고딕' },
18
+ };
19
+ function resolveColors(args) {
20
+ if (args['colors'] && typeof args['colors'] === 'object') {
21
+ const c = args['colors'];
22
+ return {
23
+ primary: c['primary'] || '#1A1A2E',
24
+ accent: c['accent'] || '#1B998B',
25
+ light: c['light'] || '#F5F5F5',
26
+ highlight: c['highlight'] || '#3CDFFF',
27
+ sidebar: c['sidebar'] || '#14514A',
28
+ };
29
+ }
30
+ const scheme = args['color_scheme'] || 'MODERN_TECH';
31
+ return COLOR_PRESETS[scheme] ?? COLOR_PRESETS['MODERN_TECH'];
32
+ }
33
+ const DEFAULT_FONTS = { title: 'Segoe UI', body: '맑은 고딕' };
34
+ function resolveFonts(args) {
35
+ if (args['fonts'] && typeof args['fonts'] === 'object') {
36
+ const f = args['fonts'];
37
+ return { title: f['title'] || 'Segoe UI', body: f['body'] || '맑은 고딕' };
38
+ }
39
+ if (typeof args['fonts'] === 'string') {
40
+ return FONT_PRESETS[args['fonts']] ?? DEFAULT_FONTS;
41
+ }
42
+ const scheme = args['color_scheme'] || 'MODERN_TECH';
43
+ return FONT_PRESETS[scheme] ?? DEFAULT_FONTS;
44
+ }
45
+ function resolveStyle(args) {
46
+ const s = args['design_style'];
47
+ if (s === 'top_band' || s === 'clean')
48
+ return s;
49
+ return 'sidebar';
50
+ }
51
+ function truncate(text, maxChars) {
52
+ if (!text || text.length <= maxChars)
53
+ return text;
54
+ return text.slice(0, maxChars - 1) + '…';
55
+ }
56
+ let layoutACount = 0;
57
+ let layoutBCount = 0;
58
+ export function resetLayoutCounters() {
59
+ layoutACount = 0;
60
+ layoutBCount = 0;
61
+ }
62
+ async function addSlideAndGetNumber() {
63
+ await powerpointClient.powerpointAddSlide(7);
64
+ const resp = await powerpointClient.powerpointGetSlideCount();
65
+ return Number(resp['slide_count'] || 1);
66
+ }
67
+ async function cleanupFailedSlide(slideNum) {
68
+ try {
69
+ await powerpointClient.powerpointDeleteSlide(slideNum);
70
+ }
71
+ catch {
72
+ }
73
+ }
74
+ async function addStyleFrame(slideNum, style, colors, fonts, title, slideNumberText, isTitleOrClosing) {
75
+ if (isTitleOrClosing)
76
+ return;
77
+ await powerpointClient.powerpointSetBackground(slideNum, { color: '#FFFFFF' });
78
+ if (style === 'sidebar') {
79
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 0, 20, 540, colors.primary);
80
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(title, 60), 50, 20, 820, 45, {
81
+ fontName: fonts.title, fontSize: 24, bold: true, fontColor: colors.primary, alignment: 'left',
82
+ });
83
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 50, 68, 200, 3, colors.accent);
84
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 520, 960, 20, colors.primary);
85
+ if (slideNumberText) {
86
+ await powerpointClient.powerpointAddTextbox(slideNum, slideNumberText, 890, 522, 50, 16, {
87
+ fontName: fonts.body, fontSize: 9, fontColor: '#FFFFFF', alignment: 'right',
88
+ });
89
+ }
90
+ }
91
+ else if (style === 'top_band') {
92
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 0, 960, 8, colors.accent);
93
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(title, 60), 50, 20, 860, 45, {
94
+ fontName: fonts.title, fontSize: 24, bold: true, fontColor: colors.primary, alignment: 'left',
95
+ });
96
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 50, 68, 200, 3, colors.accent);
97
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 530, 960, 10, colors.accent);
98
+ if (slideNumberText) {
99
+ await powerpointClient.powerpointAddTextbox(slideNum, slideNumberText, 890, 532, 50, 8, {
100
+ fontName: fonts.body, fontSize: 8, fontColor: '#FFFFFF', alignment: 'right',
101
+ });
102
+ }
103
+ }
104
+ else {
105
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(title, 60), 50, 25, 860, 45, {
106
+ fontName: fonts.title, fontSize: 24, bold: true, fontColor: colors.primary, alignment: 'left',
107
+ });
108
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 50, 73, 150, 2, colors.accent);
109
+ if (slideNumberText) {
110
+ await powerpointClient.powerpointAddTextbox(slideNum, slideNumberText, 880, 515, 60, 16, {
111
+ fontName: fonts.body, fontSize: 9, fontColor: '#999999', alignment: 'right',
112
+ });
113
+ }
114
+ }
115
+ }
116
+ function contentTop(style) {
117
+ return style === 'clean' ? 85 : 85;
118
+ }
119
+ function contentLeft(_style) {
120
+ return 50;
121
+ }
122
+ function contentWidth(style) {
123
+ return style === 'top_band' ? 860 : 820;
124
+ }
125
+ const PPT_BUILD_TITLE_SLIDE_DEF = {
126
+ type: 'function',
127
+ function: {
128
+ name: 'ppt_build_title_slide',
129
+ description: 'Build a complete title slide with styled background, decorative elements, title, subtitle, and date text. One call = one finished title slide.',
130
+ parameters: {
131
+ type: 'object',
132
+ properties: {
133
+ title: { type: 'string', description: 'Main presentation title' },
134
+ subtitle: { type: 'string', description: 'Subtitle or tagline' },
135
+ date_text: { type: 'string', description: 'Date or author line' },
136
+ color_scheme: { type: 'string', description: 'Preset name (MODERN_TECH, WARM_EXECUTIVE, CLEAN_MINIMAL, CORPORATE, NATURE_FRESH, BOLD_MODERN) or omit if using colors' },
137
+ colors: { type: 'object', description: 'Custom colors: {primary, accent, light, highlight, sidebar}', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
138
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'], description: 'Visual frame style (default: sidebar)' },
139
+ fonts: { type: 'object', description: 'Custom fonts: {title, body}', properties: { title: { type: 'string' }, body: { type: 'string' } } },
140
+ },
141
+ required: ['title'],
142
+ },
143
+ },
144
+ };
145
+ async function executeBuildTitleSlide(args) {
146
+ try {
147
+ const colors = resolveColors(args);
148
+ const fonts = resolveFonts(args);
149
+ const style = resolveStyle(args);
150
+ const title = truncate(args['title'] || 'Presentation', 50);
151
+ const subtitle = truncate(args['subtitle'] || '', 80);
152
+ const dateText = truncate(args['date_text'] || '', 60);
153
+ const slideNum = await addSlideAndGetNumber();
154
+ await powerpointClient.powerpointSetBackground(slideNum, { color: colors.primary });
155
+ if (style === 'sidebar') {
156
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 0, 20, 540, colors.sidebar);
157
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 250, 165, 460, 3, colors.highlight);
158
+ await powerpointClient.powerpointAddTextbox(slideNum, title, 50, 180, 860, 85, {
159
+ fontName: fonts.title, fontSize: 36, bold: true, fontColor: '#FFFFFF', alignment: 'center',
160
+ });
161
+ if (subtitle) {
162
+ await powerpointClient.powerpointAddTextbox(slideNum, subtitle, 50, 275, 860, 40, {
163
+ fontName: fonts.body, fontSize: 16, fontColor: colors.highlight, alignment: 'center',
164
+ });
165
+ }
166
+ if (dateText) {
167
+ await powerpointClient.powerpointAddTextbox(slideNum, dateText, 50, 320, 860, 30, {
168
+ fontName: fonts.body, fontSize: 11, fontColor: '#AAAAAA', alignment: 'center',
169
+ });
170
+ }
171
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 250, 360, 460, 3, colors.highlight);
172
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 520, 960, 20, colors.accent);
173
+ }
174
+ else if (style === 'top_band') {
175
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 0, 960, 60, colors.accent);
176
+ await powerpointClient.powerpointAddTextbox(slideNum, title, 50, 180, 860, 85, {
177
+ fontName: fonts.title, fontSize: 36, bold: true, fontColor: '#FFFFFF', alignment: 'center',
178
+ });
179
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 380, 270, 200, 3, colors.highlight);
180
+ if (subtitle) {
181
+ await powerpointClient.powerpointAddTextbox(slideNum, subtitle, 50, 285, 860, 40, {
182
+ fontName: fonts.body, fontSize: 16, fontColor: colors.highlight, alignment: 'center',
183
+ });
184
+ }
185
+ if (dateText) {
186
+ await powerpointClient.powerpointAddTextbox(slideNum, dateText, 50, 330, 860, 30, {
187
+ fontName: fonts.body, fontSize: 11, fontColor: '#AAAAAA', alignment: 'center',
188
+ });
189
+ }
190
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 530, 960, 10, colors.accent);
191
+ }
192
+ else {
193
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 380, 170, 200, 2, colors.accent);
194
+ await powerpointClient.powerpointAddTextbox(slideNum, title, 50, 185, 860, 85, {
195
+ fontName: fonts.title, fontSize: 36, bold: true, fontColor: '#FFFFFF', alignment: 'center',
196
+ });
197
+ if (subtitle) {
198
+ await powerpointClient.powerpointAddTextbox(slideNum, subtitle, 50, 280, 860, 40, {
199
+ fontName: fonts.body, fontSize: 16, fontColor: colors.highlight, alignment: 'center',
200
+ });
201
+ }
202
+ if (dateText) {
203
+ await powerpointClient.powerpointAddTextbox(slideNum, dateText, 50, 325, 860, 30, {
204
+ fontName: fonts.body, fontSize: 11, fontColor: '#AAAAAA', alignment: 'center',
205
+ });
206
+ }
207
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 380, 365, 200, 2, colors.accent);
208
+ }
209
+ return { success: true, result: `Slide ${slideNum} built: TITLE (${style} style)` };
210
+ }
211
+ catch (error) {
212
+ return { success: false, error: `Failed to build title slide: ${error instanceof Error ? error.message : String(error)}` };
213
+ }
214
+ }
215
+ const PPT_BUILD_LAYOUT_A_DEF = {
216
+ type: 'function',
217
+ function: {
218
+ name: 'ppt_build_layout_a',
219
+ description: 'Build a bullet-point slide. Use "■" for main items and " – " for sub-details in body text. Max 4 main bullets with 2-3 sub-details each.',
220
+ parameters: {
221
+ type: 'object',
222
+ properties: {
223
+ title: { type: 'string', description: 'Slide title' },
224
+ body: { type: 'string', description: 'Bullet text using ■ and – markers, separated by \\n' },
225
+ color_scheme: { type: 'string' },
226
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
227
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
228
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
229
+ slide_number_text: { type: 'string', description: 'Slide number display text' },
230
+ },
231
+ required: ['title', 'body'],
232
+ },
233
+ },
234
+ };
235
+ async function executeBuildLayoutA(args) {
236
+ layoutACount++;
237
+ if (layoutACount > 3) {
238
+ return {
239
+ success: false,
240
+ error: `Layout A limit exceeded (${layoutACount}/3 max). You MUST use a different layout: ppt_build_layout_b (two columns), ppt_build_layout_c (big number), ppt_build_layout_d (3 metrics), ppt_build_layout_e (process), or ppt_build_layout_f (table). Do NOT use ppt_build_layout_a again.`,
241
+ };
242
+ }
243
+ let slideNum = 0;
244
+ try {
245
+ const colors = resolveColors(args);
246
+ const fonts = resolveFonts(args);
247
+ const style = resolveStyle(args);
248
+ const title = args['title'] || '';
249
+ let body = truncate(args['body'] || '', 500);
250
+ if (!body.trim()) {
251
+ return { success: false, error: 'Layout A requires non-empty body text. Provide bullet content using ■ and – markers.' };
252
+ }
253
+ const hasSubDetails = body.split('\n').some(line => line.trim().startsWith('–') || line.trim().startsWith('- '));
254
+ const bodyFontSize = hasSubDetails ? 15 : 16;
255
+ if (!hasSubDetails) {
256
+ body = body.replace(/\n■/g, '\n\n■');
257
+ }
258
+ slideNum = await addSlideAndGetNumber();
259
+ await addStyleFrame(slideNum, style, colors, fonts, title, args['slide_number_text']);
260
+ const top = contentTop(style);
261
+ const left = contentLeft(style);
262
+ const width = contentWidth(style);
263
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, top, width, 420, colors.light);
264
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, top, 4, 420, colors.accent);
265
+ await powerpointClient.powerpointAddTextbox(slideNum, body, left + 20, top + 10, width - 35, 400, {
266
+ fontName: fonts.body, fontSize: bodyFontSize, fontColor: '#333333', alignment: 'left',
267
+ });
268
+ return { success: true, result: `Slide ${slideNum} built: Layout A (${style} style)` };
269
+ }
270
+ catch (error) {
271
+ if (slideNum > 0)
272
+ await cleanupFailedSlide(slideNum);
273
+ return { success: false, error: `Failed to build Layout A: ${error instanceof Error ? error.message : String(error)}` };
274
+ }
275
+ }
276
+ const PPT_BUILD_LAYOUT_B_DEF = {
277
+ type: 'function',
278
+ function: {
279
+ name: 'ppt_build_layout_b',
280
+ description: 'Build a two-column comparison slide with left/right headers and bodies. Good for before/after, pros/cons, AS-IS/TO-BE.',
281
+ parameters: {
282
+ type: 'object',
283
+ properties: {
284
+ title: { type: 'string', description: 'Slide title' },
285
+ left_header: { type: 'string', description: 'Left column header' },
286
+ right_header: { type: 'string', description: 'Right column header' },
287
+ left_body: { type: 'string', description: 'Left column body text' },
288
+ right_body: { type: 'string', description: 'Right column body text' },
289
+ color_scheme: { type: 'string' },
290
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
291
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
292
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
293
+ slide_number_text: { type: 'string' },
294
+ },
295
+ required: ['title', 'left_header', 'right_header', 'left_body', 'right_body'],
296
+ },
297
+ },
298
+ };
299
+ async function executeBuildLayoutB(args) {
300
+ layoutBCount++;
301
+ if (layoutBCount > 4) {
302
+ return {
303
+ success: false,
304
+ error: `Layout B limit exceeded (${layoutBCount}/4 max). Use other layouts: ppt_build_layout_c (big number), ppt_build_layout_d (3 metrics), ppt_build_layout_e (process), or ppt_build_layout_f (table). Do NOT use ppt_build_layout_b again.`,
305
+ };
306
+ }
307
+ let slideNum = 0;
308
+ try {
309
+ const colors = resolveColors(args);
310
+ const fonts = resolveFonts(args);
311
+ const style = resolveStyle(args);
312
+ const title = args['title'] || '';
313
+ const leftBody = args['left_body'] || '';
314
+ const rightBody = args['right_body'] || '';
315
+ if (!leftBody.trim() && !rightBody.trim()) {
316
+ return { success: false, error: 'Layout B requires non-empty body text in at least one column.' };
317
+ }
318
+ slideNum = await addSlideAndGetNumber();
319
+ await addStyleFrame(slideNum, style, colors, fonts, title, args['slide_number_text']);
320
+ const top = contentTop(style);
321
+ const left = contentLeft(style);
322
+ const width = contentWidth(style);
323
+ const colWidth = Math.floor((width - 20) / 2);
324
+ const rightLeft = left + colWidth + 20;
325
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, top, colWidth, 32, colors.light);
326
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', rightLeft, top, colWidth, 32, colors.light);
327
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['left_header'] || '', 30), left + 8, top + 2, colWidth - 16, 28, {
328
+ fontName: fonts.title, fontSize: 16, bold: true, fontColor: colors.accent, alignment: 'left',
329
+ });
330
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['right_header'] || '', 30), rightLeft + 8, top + 2, colWidth - 16, 28, {
331
+ fontName: fonts.title, fontSize: 16, bold: true, fontColor: colors.primary, alignment: 'left',
332
+ });
333
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, top + 32, colWidth, 2, colors.accent);
334
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', rightLeft, top + 32, colWidth, 2, colors.primary);
335
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left + colWidth + 8, top, 2, 350, colors.light);
336
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['left_body'] || '', 400), left, top + 40, colWidth, 350, {
337
+ fontName: fonts.body, fontSize: 15, fontColor: '#333333', alignment: 'left',
338
+ });
339
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['right_body'] || '', 400), rightLeft, top + 40, colWidth, 350, {
340
+ fontName: fonts.body, fontSize: 15, fontColor: '#333333', alignment: 'left',
341
+ });
342
+ return { success: true, result: `Slide ${slideNum} built: Layout B (${style} style)` };
343
+ }
344
+ catch (error) {
345
+ if (slideNum > 0)
346
+ await cleanupFailedSlide(slideNum);
347
+ return { success: false, error: `Failed to build Layout B: ${error instanceof Error ? error.message : String(error)}` };
348
+ }
349
+ }
350
+ const PPT_BUILD_LAYOUT_C_DEF = {
351
+ type: 'function',
352
+ function: {
353
+ name: 'ppt_build_layout_c',
354
+ description: 'Build a big-number spotlight slide. Highlights ONE key metric with a large number, label, and description.',
355
+ parameters: {
356
+ type: 'object',
357
+ properties: {
358
+ title: { type: 'string', description: 'Slide title' },
359
+ number: { type: 'string', description: 'The big number to display (e.g., "300%↑", "₩12.5억")' },
360
+ label: { type: 'string', description: 'Label below the number' },
361
+ description: { type: 'string', description: 'Explanation text (2-3 sentences)' },
362
+ color_scheme: { type: 'string' },
363
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
364
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
365
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
366
+ slide_number_text: { type: 'string' },
367
+ },
368
+ required: ['title', 'number', 'label'],
369
+ },
370
+ },
371
+ };
372
+ async function executeBuildLayoutC(args) {
373
+ let slideNum = 0;
374
+ try {
375
+ const colors = resolveColors(args);
376
+ const fonts = resolveFonts(args);
377
+ const style = resolveStyle(args);
378
+ const title = args['title'] || '';
379
+ const number = args['number'] || '';
380
+ if (!number.trim()) {
381
+ return { success: false, error: 'Layout C requires a non-empty number value.' };
382
+ }
383
+ slideNum = await addSlideAndGetNumber();
384
+ await addStyleFrame(slideNum, style, colors, fonts, title, args['slide_number_text']);
385
+ const left = contentLeft(style);
386
+ const width = contentWidth(style);
387
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, 88, width, 185, colors.light);
388
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, 88, width, 4, colors.accent);
389
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, 269, width, 2, colors.accent);
390
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['number'] || '', 12), left, 100, width, 120, {
391
+ fontName: fonts.title, fontSize: 72, bold: true, fontColor: colors.accent, alignment: 'center',
392
+ });
393
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['label'] || '', 40), left, 230, width, 35, {
394
+ fontName: fonts.body, fontSize: 18, fontColor: '#555555', alignment: 'center',
395
+ });
396
+ if (args['description']) {
397
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left + 30, 290, width - 60, 160, colors.light);
398
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['description'], 400), left + 50, 300, width - 100, 140, {
399
+ fontName: fonts.body, fontSize: 14, fontColor: '#333333', alignment: 'center',
400
+ });
401
+ }
402
+ return { success: true, result: `Slide ${slideNum} built: Layout C (${style} style)` };
403
+ }
404
+ catch (error) {
405
+ if (slideNum > 0)
406
+ await cleanupFailedSlide(slideNum);
407
+ return { success: false, error: `Failed to build Layout C: ${error instanceof Error ? error.message : String(error)}` };
408
+ }
409
+ }
410
+ const PPT_BUILD_LAYOUT_D_DEF = {
411
+ type: 'function',
412
+ function: {
413
+ name: 'ppt_build_layout_d',
414
+ description: 'Build a three-metrics dashboard slide. Shows 3 KPIs side by side with numbers, labels, descriptions, and an insight summary.',
415
+ parameters: {
416
+ type: 'object',
417
+ properties: {
418
+ title: { type: 'string', description: 'Slide title' },
419
+ metrics: {
420
+ type: 'array',
421
+ description: 'Exactly 3 metrics, each with number (max 6 chars), label, and description',
422
+ items: {
423
+ type: 'object',
424
+ properties: {
425
+ number: { type: 'string', description: 'Short numeric value (max 6 chars, e.g., "$35.7B", "96.8%")' },
426
+ label: { type: 'string', description: 'Metric label (units go here)' },
427
+ description: { type: 'string', description: '2-3 sentence description' },
428
+ },
429
+ required: ['number', 'label'],
430
+ },
431
+ },
432
+ insight_text: { type: 'string', description: 'Summary insight paragraph below metrics' },
433
+ color_scheme: { type: 'string' },
434
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
435
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
436
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
437
+ slide_number_text: { type: 'string' },
438
+ },
439
+ required: ['title', 'metrics'],
440
+ },
441
+ },
442
+ };
443
+ async function executeBuildLayoutD(args) {
444
+ let slideNum = 0;
445
+ try {
446
+ const colors = resolveColors(args);
447
+ const fonts = resolveFonts(args);
448
+ const style = resolveStyle(args);
449
+ const title = args['title'] || '';
450
+ const metrics = args['metrics'] || [];
451
+ if (metrics.length < 3) {
452
+ return { success: false, error: 'Layout D requires exactly 3 metrics' };
453
+ }
454
+ slideNum = await addSlideAndGetNumber();
455
+ await addStyleFrame(slideNum, style, colors, fonts, title, args['slide_number_text']);
456
+ const baseLeft = contentLeft(style);
457
+ const totalWidth = contentWidth(style);
458
+ const metricWidth = Math.floor((totalWidth - 40) / 3);
459
+ const positions = [
460
+ baseLeft,
461
+ baseLeft + metricWidth + 20,
462
+ baseLeft + metricWidth * 2 + 40,
463
+ ];
464
+ for (let i = 0; i < 3; i++) {
465
+ const m = metrics[i];
466
+ const x = positions[i];
467
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', x, 90, metricWidth, 260, colors.light);
468
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', x, 90, metricWidth, 4, colors.accent);
469
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(m['number'] || '', 8), x + 5, 102, metricWidth - 10, 70, {
470
+ fontName: fonts.title, fontSize: 44, bold: true, fontColor: colors.accent, alignment: 'center',
471
+ });
472
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(m['label'] || '', 20), x + 5, 175, metricWidth - 10, 25, {
473
+ fontName: fonts.body, fontSize: 13, fontColor: '#666666', alignment: 'center',
474
+ });
475
+ if (m['description']) {
476
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(m['description'], 150), x + 5, 205, metricWidth - 10, 130, {
477
+ fontName: fonts.body, fontSize: 11, fontColor: '#555555', alignment: 'center',
478
+ });
479
+ }
480
+ }
481
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', positions[1] - 11, 100, 1, 240, colors.accent);
482
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', positions[2] - 11, 100, 1, 240, colors.accent);
483
+ if (args['insight_text']) {
484
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', baseLeft, 370, totalWidth, 85, colors.light);
485
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', baseLeft, 370, 4, 85, colors.accent);
486
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['insight_text'], 250), baseLeft + 20, 380, totalWidth - 35, 65, {
487
+ fontName: fonts.body, fontSize: 13, italic: true, fontColor: colors.primary, alignment: 'left',
488
+ });
489
+ }
490
+ return { success: true, result: `Slide ${slideNum} built: Layout D (${style} style)` };
491
+ }
492
+ catch (error) {
493
+ if (slideNum > 0)
494
+ await cleanupFailedSlide(slideNum);
495
+ return { success: false, error: `Failed to build Layout D: ${error instanceof Error ? error.message : String(error)}` };
496
+ }
497
+ }
498
+ const PPT_BUILD_LAYOUT_E_DEF = {
499
+ type: 'function',
500
+ function: {
501
+ name: 'ppt_build_layout_e',
502
+ description: 'Build a process/timeline slide with exactly 3 steps shown as circles with connecting arrows. Max 3 steps.',
503
+ parameters: {
504
+ type: 'object',
505
+ properties: {
506
+ title: { type: 'string', description: 'Slide title' },
507
+ steps: {
508
+ type: 'array',
509
+ description: 'Exactly 3 steps, each with a short label (max 8 chars) and description',
510
+ items: {
511
+ type: 'object',
512
+ properties: {
513
+ label: { type: 'string', description: 'Step label (max 8 Korean chars)' },
514
+ description: { type: 'string', description: 'Step description (1-3 sentences)' },
515
+ },
516
+ required: ['label'],
517
+ },
518
+ },
519
+ insight_text: { type: 'string', description: 'Summary insight below the steps' },
520
+ color_scheme: { type: 'string' },
521
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
522
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
523
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
524
+ slide_number_text: { type: 'string' },
525
+ },
526
+ required: ['title', 'steps'],
527
+ },
528
+ },
529
+ };
530
+ async function executeBuildLayoutE(args) {
531
+ let slideNum = 0;
532
+ try {
533
+ const colors = resolveColors(args);
534
+ const fonts = resolveFonts(args);
535
+ const style = resolveStyle(args);
536
+ const title = args['title'] || '';
537
+ const steps = args['steps'] || [];
538
+ if (steps.length < 3) {
539
+ return { success: false, error: 'Layout E requires exactly 3 steps' };
540
+ }
541
+ slideNum = await addSlideAndGetNumber();
542
+ await addStyleFrame(slideNum, style, colors, fonts, title, args['slide_number_text']);
543
+ const baseLeft = contentLeft(style);
544
+ const totalWidth = contentWidth(style);
545
+ const stepWidth = 250;
546
+ const stepPositions = [
547
+ baseLeft + 10,
548
+ baseLeft + Math.floor(totalWidth / 2) - Math.floor(stepWidth / 2),
549
+ baseLeft + totalWidth - stepWidth - 10,
550
+ ];
551
+ const circleSize = 60;
552
+ for (let i = 0; i < 3; i++) {
553
+ const step = steps[i];
554
+ const x = stepPositions[i];
555
+ const circleX = x + Math.floor(stepWidth / 2) - Math.floor(circleSize / 2);
556
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', x, 170, stepWidth, 160, colors.light);
557
+ await powerpointClient.powerpointAddShape(slideNum, 'oval', circleX, 100, circleSize, circleSize, colors.accent);
558
+ await powerpointClient.powerpointAddTextbox(slideNum, String(i + 1), circleX, 110, circleSize, 40, {
559
+ fontName: fonts.title, fontSize: 22, bold: true, fontColor: '#FFFFFF', alignment: 'center',
560
+ });
561
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(step['label'] || '', 12), x + 10, 175, stepWidth - 20, 28, {
562
+ fontName: fonts.title, fontSize: 14, bold: true, fontColor: colors.primary, alignment: 'center',
563
+ });
564
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', x + 60, 203, stepWidth - 120, 2, colors.accent);
565
+ if (step['description']) {
566
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(step['description'], 150), x + 10, 210, stepWidth - 20, 115, {
567
+ fontName: fonts.body, fontSize: 11, fontColor: '#555555', alignment: 'center',
568
+ });
569
+ }
570
+ if (i < 2) {
571
+ const arrowX = circleX + circleSize + 5;
572
+ const nextCircleX = stepPositions[i + 1] + Math.floor(stepWidth / 2) - Math.floor(circleSize / 2);
573
+ const arrowWidth = nextCircleX - arrowX - 5;
574
+ if (arrowWidth > 0) {
575
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', arrowX, 128, arrowWidth, 3, colors.highlight);
576
+ }
577
+ }
578
+ }
579
+ if (args['insight_text']) {
580
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', baseLeft, 350, totalWidth, 85, colors.light);
581
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', baseLeft, 350, 4, 85, colors.accent);
582
+ await powerpointClient.powerpointAddTextbox(slideNum, truncate(args['insight_text'], 200), baseLeft + 20, 360, totalWidth - 35, 65, {
583
+ fontName: fonts.body, fontSize: 13, italic: true, fontColor: colors.primary, alignment: 'left',
584
+ });
585
+ }
586
+ return { success: true, result: `Slide ${slideNum} built: Layout E (${style} style)` };
587
+ }
588
+ catch (error) {
589
+ if (slideNum > 0)
590
+ await cleanupFailedSlide(slideNum);
591
+ return { success: false, error: `Failed to build Layout E: ${error instanceof Error ? error.message : String(error)}` };
592
+ }
593
+ }
594
+ const PPT_BUILD_LAYOUT_F_DEF = {
595
+ type: 'function',
596
+ function: {
597
+ name: 'ppt_build_layout_f',
598
+ description: 'Build a table slide. Provide table_data as a 2D array where row 0 is headers. Automatically styles header row and alternating rows.',
599
+ parameters: {
600
+ type: 'object',
601
+ properties: {
602
+ title: { type: 'string', description: 'Slide title' },
603
+ table_data: {
604
+ type: 'array',
605
+ description: '2D array: first row = headers, remaining rows = data. Example: [["Name","Score"],["Alice","95"],["Bob","88"]]',
606
+ items: { type: 'array', items: { type: 'string' } },
607
+ },
608
+ color_scheme: { type: 'string' },
609
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
610
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
611
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
612
+ slide_number_text: { type: 'string' },
613
+ },
614
+ required: ['title', 'table_data'],
615
+ },
616
+ },
617
+ };
618
+ async function executeBuildLayoutF(args) {
619
+ let slideNum = 0;
620
+ try {
621
+ const colors = resolveColors(args);
622
+ const style = resolveStyle(args);
623
+ const fonts = resolveFonts(args);
624
+ const title = args['title'] || '';
625
+ const tableData = args['table_data'] || [];
626
+ if (tableData.length < 2) {
627
+ return { success: false, error: 'Layout F requires at least 2 rows (1 header + 1 data)' };
628
+ }
629
+ slideNum = await addSlideAndGetNumber();
630
+ await addStyleFrame(slideNum, style, colors, fonts, title, args['slide_number_text']);
631
+ const left = contentLeft(style);
632
+ const width = contentWidth(style);
633
+ const rows = tableData.length;
634
+ const cols = tableData[0].length;
635
+ const truncatedData = tableData.map(row => row.map(cell => truncate(cell || '', 50)));
636
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', left, 83, width, 2, colors.accent);
637
+ await powerpointClient.powerpointAddTable(slideNum, rows, cols, left, 88, width, 345, truncatedData);
638
+ const shapeList = await powerpointClient.powerpointGetShapeList(slideNum);
639
+ const shapeCount = Number(shapeList?.['count'] ?? (Array.isArray(shapeList?.['shapes']) ? shapeList['shapes'].length : 0));
640
+ if (shapeCount > 0) {
641
+ await powerpointClient.powerpointSetTableStyle(slideNum, shapeCount, {
642
+ headerRowFill: colors.accent,
643
+ alternateRowFill: colors.light,
644
+ borderColor: '#D0D0D0',
645
+ });
646
+ }
647
+ return { success: true, result: `Slide ${slideNum} built: Layout F table ${rows}×${cols} (${style} style)` };
648
+ }
649
+ catch (error) {
650
+ if (slideNum > 0)
651
+ await cleanupFailedSlide(slideNum);
652
+ return { success: false, error: `Failed to build Layout F: ${error instanceof Error ? error.message : String(error)}` };
653
+ }
654
+ }
655
+ const PPT_BUILD_CLOSING_SLIDE_DEF = {
656
+ type: 'function',
657
+ function: {
658
+ name: 'ppt_build_closing_slide',
659
+ description: 'Build a farewell/thank-you closing slide with dark background. ONLY use as the absolute LAST slide before save. Do NOT use for ANY content sections. text parameter: ONLY short farewell like "감사합니다" or "Thank You" (max 15 chars). NEVER put long sentences in text.',
660
+ parameters: {
661
+ type: 'object',
662
+ properties: {
663
+ text: { type: 'string', description: 'Main closing text (e.g., "감사합니다", "Thank You")' },
664
+ subtitle: { type: 'string', description: 'Contact info or tagline' },
665
+ color_scheme: { type: 'string' },
666
+ colors: { type: 'object', properties: { primary: { type: 'string' }, accent: { type: 'string' }, light: { type: 'string' }, highlight: { type: 'string' }, sidebar: { type: 'string' } } },
667
+ design_style: { type: 'string', enum: ['sidebar', 'top_band', 'clean'] },
668
+ fonts: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } } },
669
+ },
670
+ required: ['text'],
671
+ },
672
+ },
673
+ };
674
+ async function executeBuildClosingSlide(args) {
675
+ try {
676
+ const colors = resolveColors(args);
677
+ const fonts = resolveFonts(args);
678
+ const style = resolveStyle(args);
679
+ const text = truncate(args['text'] || '감사합니다', 30);
680
+ const subtitle = truncate(args['subtitle'] || '', 150);
681
+ const slideNum = await addSlideAndGetNumber();
682
+ await powerpointClient.powerpointSetBackground(slideNum, { color: colors.primary });
683
+ if (style === 'sidebar') {
684
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 0, 20, 540, colors.accent);
685
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 250, 190, 460, 3, colors.highlight);
686
+ await powerpointClient.powerpointAddTextbox(slideNum, text, 50, 200, 860, 80, {
687
+ fontName: fonts.title, fontSize: 42, bold: true, fontColor: '#FFFFFF', alignment: 'center',
688
+ });
689
+ if (subtitle) {
690
+ await powerpointClient.powerpointAddTextbox(slideNum, subtitle, 50, 290, 860, 80, {
691
+ fontName: fonts.body, fontSize: 14, fontColor: colors.highlight, alignment: 'center',
692
+ });
693
+ }
694
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 250, 380, 460, 3, colors.highlight);
695
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 520, 960, 20, colors.accent);
696
+ }
697
+ else if (style === 'top_band') {
698
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 0, 960, 60, colors.accent);
699
+ await powerpointClient.powerpointAddTextbox(slideNum, text, 50, 200, 860, 80, {
700
+ fontName: fonts.title, fontSize: 42, bold: true, fontColor: '#FFFFFF', alignment: 'center',
701
+ });
702
+ if (subtitle) {
703
+ await powerpointClient.powerpointAddTextbox(slideNum, subtitle, 50, 290, 860, 80, {
704
+ fontName: fonts.body, fontSize: 14, fontColor: colors.highlight, alignment: 'center',
705
+ });
706
+ }
707
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 0, 530, 960, 10, colors.accent);
708
+ }
709
+ else {
710
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 380, 190, 200, 2, colors.accent);
711
+ await powerpointClient.powerpointAddTextbox(slideNum, text, 50, 200, 860, 80, {
712
+ fontName: fonts.title, fontSize: 42, bold: true, fontColor: '#FFFFFF', alignment: 'center',
713
+ });
714
+ if (subtitle) {
715
+ await powerpointClient.powerpointAddTextbox(slideNum, subtitle, 50, 290, 860, 80, {
716
+ fontName: fonts.body, fontSize: 14, fontColor: colors.highlight, alignment: 'center',
717
+ });
718
+ }
719
+ await powerpointClient.powerpointAddShape(slideNum, 'rectangle', 380, 380, 200, 2, colors.accent);
720
+ }
721
+ return { success: true, result: `Slide ${slideNum} built: CLOSING (${style} style)` };
722
+ }
723
+ catch (error) {
724
+ return { success: false, error: `Failed to build closing slide: ${error instanceof Error ? error.message : String(error)}` };
725
+ }
726
+ }
727
+ export const pptBuildTitleSlideTool = {
728
+ definition: PPT_BUILD_TITLE_SLIDE_DEF,
729
+ execute: executeBuildTitleSlide,
730
+ categories: OFFICE_CATEGORIES,
731
+ description: 'Build complete title slide',
732
+ };
733
+ export const pptBuildLayoutATool = {
734
+ definition: PPT_BUILD_LAYOUT_A_DEF,
735
+ execute: executeBuildLayoutA,
736
+ categories: OFFICE_CATEGORIES,
737
+ description: 'Build bullet-point slide',
738
+ };
739
+ export const pptBuildLayoutBTool = {
740
+ definition: PPT_BUILD_LAYOUT_B_DEF,
741
+ execute: executeBuildLayoutB,
742
+ categories: OFFICE_CATEGORIES,
743
+ description: 'Build two-column slide',
744
+ };
745
+ export const pptBuildLayoutCTool = {
746
+ definition: PPT_BUILD_LAYOUT_C_DEF,
747
+ execute: executeBuildLayoutC,
748
+ categories: OFFICE_CATEGORIES,
749
+ description: 'Build big-number slide',
750
+ };
751
+ export const pptBuildLayoutDTool = {
752
+ definition: PPT_BUILD_LAYOUT_D_DEF,
753
+ execute: executeBuildLayoutD,
754
+ categories: OFFICE_CATEGORIES,
755
+ description: 'Build three-metrics slide',
756
+ };
757
+ export const pptBuildLayoutETool = {
758
+ definition: PPT_BUILD_LAYOUT_E_DEF,
759
+ execute: executeBuildLayoutE,
760
+ categories: OFFICE_CATEGORIES,
761
+ description: 'Build process/timeline slide',
762
+ };
763
+ export const pptBuildLayoutFTool = {
764
+ definition: PPT_BUILD_LAYOUT_F_DEF,
765
+ execute: executeBuildLayoutF,
766
+ categories: OFFICE_CATEGORIES,
767
+ description: 'Build table slide',
768
+ };
769
+ export const pptBuildClosingSlideTool = {
770
+ definition: PPT_BUILD_CLOSING_SLIDE_DEF,
771
+ execute: executeBuildClosingSlide,
772
+ categories: OFFICE_CATEGORIES,
773
+ description: 'Build closing slide',
774
+ };
775
+ export const layoutBuilderTools = [
776
+ pptBuildTitleSlideTool,
777
+ pptBuildLayoutATool,
778
+ pptBuildLayoutBTool,
779
+ pptBuildLayoutCTool,
780
+ pptBuildLayoutDTool,
781
+ pptBuildLayoutETool,
782
+ pptBuildLayoutFTool,
783
+ pptBuildClosingSlideTool,
784
+ ];
785
+ //# sourceMappingURL=layout-builders.js.map