opencode-sa-assistant 0.2.5 β†’ 0.2.6

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/README.md CHANGED
@@ -17,6 +17,9 @@ This plugin activates when you use the `wadd` keyword in your messages, transfor
17
17
  - `sa-researcher`: AWS service information via MCP tools
18
18
  - `sa-reviewer`: Well-Architected Framework reviews (6 pillars)
19
19
 
20
+ **Orchestrator**:
21
+ - `sa-orchestrator`: Master agent that coordinates Gurus and Specialists based on Guru_Mandate rules
22
+
20
23
  ## Features
21
24
 
22
25
  ✨ **WADD Mode Activation**: Type `wadd` to activate SA mode
@@ -26,6 +29,33 @@ This plugin activates when you use the `wadd` keyword in your messages, transfor
26
29
  πŸ“š **AWS MCP Tools**: Real-time AWS documentation access
27
30
  πŸ‡°πŸ‡· **Bilingual**: Korean prose + English technical terms
28
31
 
32
+ ## Agents
33
+
34
+ ### Guru Agents (Philosophy & Strategy)
35
+
36
+ | Agent | Core Principles | Use When | Avoid When |
37
+ |-------|----------------|----------|------------|
38
+ | **sa-bezos** | Customer Obsession, Day 1, Working Backwards | Customer value definition, Long-term vs short-term decisions, PR/FAQ writing | Pure technical implementation, Cost calculations |
39
+ | **sa-vogels** | Everything Fails, Design for Failure, Loose Coupling | Distributed system design, Scalability/resilience patterns, Operational excellence | Business strategy, Cost ROI analysis |
40
+ | **sa-naval** | Leverage, Asymmetric Outcomes, Compounding | Cost analysis, ROI, Leverage points identification, Complexity removal | Technical architecture details, Customer experience |
41
+ | **sa-feynman** | Feynman Technique, First Principles, Simplicity | Complex concept simplification, Non-technical presentations, Learning materials | Detailed technical decisions, Cost analysis |
42
+
43
+ ### Specialist Agents (Execution)
44
+
45
+ | Agent | Role | Output |
46
+ |-------|------|--------|
47
+ | **sa-explorer** | Architecture analysis | Component discovery, Dependency mapping, As-Is documentation |
48
+ | **sa-researcher** | AWS information gathering | Service info, Pricing, Best practices with evidence |
49
+ | **sa-reviewer** | Well-Architected review | 6 Pillars evaluation, Risk summary, Prioritized recommendations |
50
+
51
+ ### Orchestrator
52
+
53
+ **sa-orchestrator** is the master agent that:
54
+ - Classifies user intent (Architecture Review, Service Selection, Cost Analysis, etc.)
55
+ - Invokes appropriate Gurus based on Guru_Mandate rules
56
+ - Delegates tasks to Specialists
57
+ - Enforces anti-patterns (no architecture decisions without Guru consultation)
58
+
29
59
  ## Installation
30
60
 
31
61
  Add the plugin to your `opencode.json`:
@@ -107,17 +137,18 @@ packages/opencode-sa-assistant/
107
137
  β”œβ”€β”€ src/
108
138
  β”‚ β”œβ”€β”€ index.ts # Plugin entry point
109
139
  β”‚ β”œβ”€β”€ hooks/wadd-mode.ts # WADD keyword detection
110
- β”‚ β”œβ”€β”€ prompts/
111
- β”‚ β”‚ β”œβ”€β”€ orchestrator.ts # SA Orchestrator
112
- β”‚ β”‚ β”œβ”€β”€ gurus.ts # 4 Guru agents
113
- β”‚ β”‚ └── specialists.ts # 3 Specialist agents
114
- β”‚ β”œβ”€β”€ skills/
115
- β”‚ β”‚ β”œβ”€β”€ guru/SKILL.md
116
- β”‚ β”‚ β”œβ”€β”€ mcp/SKILL.md
117
- β”‚ β”‚ β”œβ”€β”€ docx/SKILL.md
118
- β”‚ β”‚ └── pptx/SKILL.md
119
- β”‚ └── __tests__/
120
- β”‚ └── *.test.ts # 65 tests
140
+ β”‚ β”œβ”€β”€ agents/index.ts # Agent/Skill installation
141
+ β”‚ β”‚ β”œβ”€β”€ prompts/
142
+ β”‚ β”‚ β”‚ β”œβ”€β”€ orchestrator.ts # SA Orchestrator
143
+ β”‚ β”‚ β”‚ β”œβ”€β”€ gurus.ts # 4 Guru agents
144
+ β”‚ β”‚ β”‚ └── specialists.ts # 3 Specialist agents
145
+ β”‚ β”‚ β”œβ”€β”€ skills/
146
+ β”‚ β”‚ β”‚ β”œβ”€β”€ guru/SKILL.md
147
+ β”‚ β”‚ β”‚ β”œβ”€β”€ mcp/SKILL.md
148
+ β”‚ β”‚ β”‚ β”œβ”€β”€ docx/SKILL.md
149
+ β”‚ β”‚ β”‚ └── pptx/SKILL.md
150
+ β”‚ β”‚ └── __tests__/
151
+ β”‚ β”‚ └── *.test.ts # 80 tests
121
152
  β”œβ”€β”€ package.json
122
153
  β”œβ”€β”€ tsconfig.json
123
154
  └── bunfig.toml
@@ -151,7 +182,7 @@ The plugin integrates with AWS Documentation MCP for real-time information:
151
182
 
152
183
  ## License
153
184
 
154
- [Your License Here]
185
+ MIT
155
186
 
156
187
  ## Author
157
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-sa-assistant",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "description": "OpenCode plugin for AWS Solutions Architect assistant with multi-agent Guru system",
@@ -272,6 +272,218 @@ new Paragraph({
272
272
  })
273
273
  ```
274
274
 
275
+ ### 이미지 λΉ„μœ¨ μœ μ§€ μ‚½μž…
276
+
277
+ 이미지 원본 λΉ„μœ¨μ„ μœ μ§€ν•˜λ©΄μ„œ μ‚½μž…ν•˜λ €λ©΄ `image-size` νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.
278
+
279
+ ```javascript
280
+ const sizeOf = require('image-size');
281
+
282
+ function createImageWithAspectRatio(imagePath, maxWidth = 500) {
283
+ const dimensions = sizeOf(imagePath);
284
+ const ratio = dimensions.height / dimensions.width;
285
+ const displayWidth = Math.min(maxWidth, dimensions.width);
286
+ const displayHeight = Math.round(displayWidth * ratio);
287
+
288
+ return new Paragraph({
289
+ alignment: AlignmentType.CENTER,
290
+ children: [new ImageRun({
291
+ type: imagePath.endsWith('.png') ? 'png' : 'jpg',
292
+ data: fs.readFileSync(imagePath),
293
+ transformation: { width: displayWidth, height: displayHeight }
294
+ })]
295
+ });
296
+ }
297
+
298
+ // μ‚¬μš© μ˜ˆμ‹œ
299
+ createImageWithAspectRatio('diagrams/architecture.png', 500)
300
+ ```
301
+
302
+ ## λ‹€μ΄μ–΄κ·Έλž¨ μ‚½μž…
303
+
304
+ ### Mermaid λ‹€μ΄μ–΄κ·Έλž¨μ„ μ΄λ―Έμ§€λ‘œ λ³€ν™˜
305
+
306
+ Word λ¬Έμ„œμ—μ„œ Mermaid λ‹€μ΄μ–΄κ·Έλž¨μ„ ν‘œμ‹œν•˜λ €λ©΄ λ¨Όμ € μ΄λ―Έμ§€λ‘œ λ³€ν™˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.
307
+
308
+ #### 1. Mermaid CLI μ„€μΉ˜
309
+
310
+ ```bash
311
+ npm install @mermaid-js/mermaid-cli
312
+ ```
313
+
314
+ #### 2. .mmd 파일 μž‘μ„±
315
+
316
+ ```mermaid
317
+ graph TB
318
+ Client[Client] --> ALB[Application Load Balancer]
319
+ ALB --> Lambda[AWS Lambda]
320
+ Lambda --> DynamoDB[(DynamoDB)]
321
+ ```
322
+
323
+ #### 3. μ΄λ―Έμ§€λ‘œ λ³€ν™˜
324
+
325
+ ```bash
326
+ npx mmdc -i diagram.mmd -o diagram.png -w 800 -b white
327
+ ```
328
+
329
+ #### 4. λΉ„μœ¨ μœ μ§€ν•˜μ—¬ μ‚½μž…
330
+
331
+ ```javascript
332
+ const sizeOf = require('image-size');
333
+
334
+ function insertMermaidDiagram(mmdPath, outputPngPath, maxWidth = 500) {
335
+ // 1. Mermaid β†’ PNG λ³€ν™˜ (사전 μ‹€ν–‰ ν•„μš”)
336
+ // npx mmdc -i ${mmdPath} -o ${outputPngPath} -w 800 -b white
337
+
338
+ // 2. 이미지 크기 확인 및 λΉ„μœ¨ 계산
339
+ const dimensions = sizeOf(outputPngPath);
340
+ const ratio = dimensions.height / dimensions.width;
341
+ const displayHeight = Math.round(maxWidth * ratio);
342
+
343
+ return new Paragraph({
344
+ alignment: AlignmentType.CENTER,
345
+ children: [new ImageRun({
346
+ type: "png",
347
+ data: fs.readFileSync(outputPngPath),
348
+ transformation: { width: maxWidth, height: displayHeight }
349
+ })]
350
+ });
351
+ }
352
+ ```
353
+
354
+ ## μ½”λ“œ 블둝 (멀티라인)
355
+
356
+ **μ€‘μš”**: Wordμ—μ„œλŠ” `\n` λ¬Έμžκ°€ μ œλŒ€λ‘œ λ Œλ”λ§λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 각 쀄을 별도 Paragraph둜 뢄리해야 ν•©λ‹ˆλ‹€.
357
+
358
+ ### 잘λͺ»λœ 방식 (λ™μž‘ μ•ˆν•¨)
359
+
360
+ ```javascript
361
+ // ❌ 이 방식은 μ€„λ°”κΏˆμ΄ λ¬΄μ‹œλ¨
362
+ new Paragraph({
363
+ children: [new TextRun({ text: "line1\nline2\nline3" })]
364
+ })
365
+ ```
366
+
367
+ ### μ˜¬λ°”λ₯Έ 방식
368
+
369
+ ```javascript
370
+ // βœ… 각 쀄을 별도 Paragraph둜 뢄리
371
+ function createCodeBlock(codeLines) {
372
+ const lines = Array.isArray(codeLines) ? codeLines : codeLines.split('\n');
373
+ return lines.map((line, index) =>
374
+ new Paragraph({
375
+ shading: { fill: "F5F5F5", type: ShadingType.CLEAR },
376
+ spacing: {
377
+ before: index === 0 ? 120 : 0,
378
+ after: index === lines.length - 1 ? 120 : 0
379
+ },
380
+ indent: { left: 360 },
381
+ children: [new TextRun({
382
+ text: line || " ", // 빈 쀄은 곡백으둜
383
+ font: "Courier New",
384
+ size: 18
385
+ })]
386
+ })
387
+ );
388
+ }
389
+
390
+ // μ‚¬μš© μ˜ˆμ‹œ - λ°°μ—΄λ‘œ 전달
391
+ ...createCodeBlock([
392
+ "{",
393
+ " \"key\": \"value\",",
394
+ " \"nested\": {",
395
+ " \"item\": 123",
396
+ " }",
397
+ "}"
398
+ ])
399
+
400
+ // λ˜λŠ” λ¬Έμžμ—΄λ‘œ 전달
401
+ ...createCodeBlock(`function hello() {
402
+ console.log("Hello, World!");
403
+ }`)
404
+ ```
405
+
406
+ ## Pretty JSON ν‘œν˜„
407
+
408
+ JSON 데이터λ₯Ό 가독성 μ’‹κ²Œ ν‘œν˜„ν•˜λŠ” 헬퍼 ν•¨μˆ˜:
409
+
410
+ ```javascript
411
+ function jsonToCodeBlock(jsonObject, indent = 4) {
412
+ const prettyJson = JSON.stringify(jsonObject, null, indent);
413
+ return createCodeBlock(prettyJson.split('\n'));
414
+ }
415
+
416
+ // μ‚¬μš© μ˜ˆμ‹œ
417
+ const apiResponse = {
418
+ userId: "user_abc123",
419
+ metadata: {
420
+ model: "claude-3-haiku",
421
+ tokens: 150
422
+ }
423
+ };
424
+
425
+ // λ¬Έμ„œμ— μΆ”κ°€
426
+ ...jsonToCodeBlock(apiResponse)
427
+ ```
428
+
429
+ ## ν…Œμ΄λΈ” μ—΄ λ„ˆλΉ„ μ‘°μ •
430
+
431
+ ### νΌμ„ΌνŠΈ 기반 μ—΄ λ„ˆλΉ„ μ§€μ •
432
+
433
+ ```javascript
434
+ function createTableWithWidths(headers, rows, widthPercents) {
435
+ const totalWidth = 9360; // DXA λ‹¨μœ„ (μ•½ 6.5인치, 일반적인 λ³Έλ¬Έ λ„ˆλΉ„)
436
+ const colWidths = widthPercents.map(p => Math.floor(totalWidth * p / 100));
437
+
438
+ const tableBorder = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" };
439
+ const cellBorders = { top: tableBorder, bottom: tableBorder, left: tableBorder, right: tableBorder };
440
+
441
+ // 헀더 ν–‰ 생성
442
+ const headerRow = new TableRow({
443
+ tableHeader: true,
444
+ children: headers.map((header, i) =>
445
+ new TableCell({
446
+ width: { size: colWidths[i], type: WidthType.DXA },
447
+ borders: cellBorders,
448
+ shading: { fill: "232F3E", type: ShadingType.CLEAR },
449
+ children: [new Paragraph({
450
+ children: [new TextRun({ text: header, bold: true, color: "FFFFFF" })]
451
+ })]
452
+ })
453
+ )
454
+ });
455
+
456
+ // 데이터 ν–‰ 생성
457
+ const dataRows = rows.map(row =>
458
+ new TableRow({
459
+ children: row.map((cell, i) =>
460
+ new TableCell({
461
+ width: { size: colWidths[i], type: WidthType.DXA },
462
+ borders: cellBorders,
463
+ children: [new Paragraph({ children: [new TextRun(cell)] })]
464
+ })
465
+ )
466
+ })
467
+ );
468
+
469
+ return new Table({
470
+ columnWidths: colWidths,
471
+ rows: [headerRow, ...dataRows]
472
+ });
473
+ }
474
+
475
+ // μ‚¬μš© μ˜ˆμ‹œ: 합이 100%κ°€ λ˜λ„λ‘ μ§€μ •
476
+ createTableWithWidths(
477
+ ["λͺ¨λΈ", "Input λΉ„μš©", "Output λΉ„μš©"],
478
+ [
479
+ ["Claude 3 Haiku", "$0.25/1M", "$1.25/1M"],
480
+ ["Claude 3 Sonnet", "$3.00/1M", "$15.00/1M"],
481
+ ["Claude 3 Opus", "$15.00/1M", "$75.00/1M"]
482
+ ],
483
+ [40, 30, 30] // 40% + 30% + 30% = 100%
484
+ )
485
+ ```
486
+
275
487
  ## AWS Blog μŠ€νƒ€μΌ κ°€μ΄λ“œ
276
488
 
277
489
  ### μž‘μ„± 원칙
@@ -305,3 +517,5 @@ new Paragraph({
305
517
  ν•„μˆ˜ μ˜μ‘΄μ„±:
306
518
  - **docx**: `npm install docx` (λ¬Έμ„œ 생성)
307
519
  - **sharp**: `npm install sharp` (이미지 처리)
520
+ - **image-size**: `npm install image-size` (이미지 크기 확인)
521
+ - **@mermaid-js/mermaid-cli**: `npm install @mermaid-js/mermaid-cli` (λ‹€μ΄μ–΄κ·Έλž¨ λ Œλ”λ§)
@@ -16,7 +16,6 @@ A user may ask you to create, edit, or analyze the contents of a .pptx file. A .
16
16
  If you just need to read the text contents of a presentation, you should convert the document to markdown:
17
17
 
18
18
  ```bash
19
- # Convert document to markdown
20
19
  python -m markitdown path-to-file.pptx
21
20
  ```
22
21
 
@@ -24,7 +23,9 @@ python -m markitdown path-to-file.pptx
24
23
  You need raw XML access for: comments, speaker notes, slide layouts, animations, design elements, and complex formatting. For any of these features, you'll need to unpack a presentation and read its raw XML contents.
25
24
 
26
25
  #### Unpacking a file
27
- `python ooxml/scripts/unpack.py <office_file> <output_dir>`
26
+ ```bash
27
+ python scripts/ooxml/unpack.py <office_file> <output_dir>
28
+ ```
28
29
 
29
30
  #### Key file structures
30
31
  * `ppt/presentation.xml` - Main presentation metadata and slide references
@@ -40,6 +41,36 @@ You need raw XML access for: comments, speaker notes, slide layouts, animations,
40
41
 
41
42
  When creating a new PowerPoint presentation from scratch, use the **html2pptx** workflow to convert HTML slides to PowerPoint with accurate positioning.
42
43
 
44
+ ### Layout Initialization (MANDATORY)
45
+
46
+ **CRITICAL**: Always define layout constants BEFORE creating slides. Different layouts have different dimensions:
47
+
48
+ | Layout | Width | Height | Use Case |
49
+ |--------|-------|--------|----------|
50
+ | `LAYOUT_16x9` | 10" | 5.625" | Standard widescreen |
51
+ | `LAYOUT_WIDE` | 13.33" | 7.5" | Extra wide presentations |
52
+ | `LAYOUT_4x3` | 10" | 7.5" | Legacy/square displays |
53
+
54
+ ```javascript
55
+ const LAYOUT_NAME = 'LAYOUT_WIDE';
56
+ const SLIDE = { width: 13.33, height: 7.5 };
57
+ const MARGIN = 0.5;
58
+ const CONTENT = {
59
+ width: SLIDE.width - (2 * MARGIN),
60
+ height: SLIDE.height - (2 * MARGIN)
61
+ };
62
+
63
+ let pptx = new PptxGenJS();
64
+ pptx.layout = LAYOUT_NAME;
65
+
66
+ slide.addText('Title', {
67
+ x: MARGIN,
68
+ y: MARGIN,
69
+ w: CONTENT.width,
70
+ h: 0.8
71
+ });
72
+ ```
73
+
43
74
  ### Design Principles
44
75
 
45
76
  **CRITICAL**: Before creating any presentation, analyze the content and choose appropriate design elements:
@@ -73,31 +104,62 @@ AWS λΈŒλžœλ“œ 색상 νŒ”λ ˆνŠΈ, ν‘œμ€€ μŠ¬λΌμ΄λ“œ ꡬ성, HTML ν…œν”Œλ¦Ώ 예
73
104
  When editing slides in an existing PowerPoint presentation, you need to work with the raw Office Open XML (OOXML) format.
74
105
 
75
106
  ### Workflow
76
- 1. Unpack the presentation: `python ooxml/scripts/unpack.py <office_file> <output_dir>`
107
+ 1. Unpack the presentation: `python scripts/ooxml/unpack.py <office_file> <output_dir>`
77
108
  2. Edit the XML files (primarily `ppt/slides/slide{N}.xml` and related files)
78
109
  3. **CRITICAL**: Validate immediately after each edit
79
- 4. Pack the final presentation: `python ooxml/scripts/pack.py <input_directory> <office_file>`
110
+ 4. Pack the final presentation: `python scripts/ooxml/pack.py <input_directory> <office_file>`
80
111
 
81
112
  ## Creating a new PowerPoint presentation **using a template**
82
113
 
83
114
  When you need to create a presentation that follows an existing template's design:
84
115
 
116
+ ### Template Workflow Scripts
117
+
118
+ All scripts are located in the `scripts/` directory of this skill.
119
+
120
+ | Script | Command | Purpose |
121
+ |--------|---------|---------|
122
+ | `thumbnail.py` | `python scripts/thumbnail.py template.pptx` | Generate slide thumbnails for visual inspection |
123
+ | `inventory.py` | `python scripts/inventory.py template.pptx -o inventory.json` | Extract all text elements with shape IDs |
124
+ | `replace.py` | `python scripts/replace.py template.pptx mapping.json -o output.pptx` | Replace text using JSON mapping |
125
+ | `rearrange.py` | `python scripts/rearrange.py template.pptx operations.json -o output.pptx` | Duplicate/delete/reorder slides |
126
+
85
127
  ### Workflow
128
+
86
129
  1. **Extract template text AND create visual thumbnail grid**:
87
- * Extract text: `python -m markitdown template.pptx > template-content.md`
88
- * Create thumbnail grids: `python scripts/thumbnail.py template.pptx`
130
+ ```bash
131
+ python -m markitdown template.pptx > template-content.md
132
+ python scripts/thumbnail.py template.pptx --output-dir ./thumbs
133
+ ```
89
134
 
90
- 2. **Analyze template and save inventory to a file**
135
+ 2. **Extract text inventory** (get shape IDs and text content):
136
+ ```bash
137
+ python scripts/inventory.py template.pptx -o inventory.json
138
+ ```
91
139
 
92
140
  3. **Create presentation outline based on template inventory**
93
141
 
94
- 4. **Duplicate, reorder, and delete slides using `rearrange.py`**
95
-
96
- 5. **Extract ALL text using the `inventory.py` script**
142
+ 4. **Duplicate, reorder, and delete slides**:
143
+ ```bash
144
+ # Create operations.json:
145
+ # {"operations": [{"action": "duplicate", "slide": 1, "count": 3}, {"action": "delete", "slides": [5, 6]}]}
146
+ python scripts/rearrange.py template.pptx operations.json -o structured.pptx
147
+ ```
97
148
 
98
- 6. **Generate replacement text and save the data to a JSON file**
149
+ 5. **Generate replacement mapping** and save to JSON:
150
+ ```json
151
+ {
152
+ "replacements": [
153
+ {"from": "Template Title", "to": "My Presentation"},
154
+ {"from": "Placeholder Text", "to": "Actual Content"}
155
+ ]
156
+ }
157
+ ```
99
158
 
100
- 7. **Apply replacements using the `replace.py` script**
159
+ 6. **Apply replacements**:
160
+ ```bash
161
+ python scripts/replace.py structured.pptx mapping.json -o final.pptx
162
+ ```
101
163
 
102
164
  ## Dependencies
103
165
 
@@ -106,5 +168,5 @@ Required dependencies:
106
168
  - **pptxgenjs**: `npm install -g pptxgenjs` (for creating presentations)
107
169
  - **playwright**: `npm install -g playwright` (for HTML rendering)
108
170
  - **sharp**: `npm install -g sharp` (for SVG rasterization)
109
- - **LibreOffice**: For PDF conversion
110
- - **Poppler**: For pdftoppm
171
+ - **LibreOffice**: For PDF conversion (thumbnail generation)
172
+ - **Poppler**: For pdftoppm (thumbnail generation)
@@ -0,0 +1,191 @@
1
+ # HTML to PowerPoint Guide
2
+
3
+ Convert HTML slides to PowerPoint presentations with accurate positioning using the `html2pptx.js` library.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Creating HTML Slides](#creating-html-slides)
8
+ 2. [Using the html2pptx Library](#using-the-html2pptx-library)
9
+ 3. [Using PptxGenJS](#using-pptxgenjs)
10
+
11
+ ---
12
+
13
+ ## Creating HTML Slides
14
+
15
+ Every HTML slide must include proper body dimensions:
16
+
17
+ ### Layout Dimensions
18
+
19
+ - **16:9** (default): `width: 720pt; height: 405pt`
20
+ - **4:3**: `width: 720pt; height: 540pt`
21
+ - **16:10**: `width: 720pt; height: 450pt`
22
+
23
+ ### Supported Elements
24
+
25
+ - `<p>`, `<h1>`-`<h6>` - Text with styling
26
+ - `<ul>`, `<ol>` - Lists (never use manual bullets β€’, -, *)
27
+ - `<b>`, `<strong>` - Bold text (inline formatting)
28
+ - `<i>`, `<em>` - Italic text (inline formatting)
29
+ - `<u>` - Underlined text (inline formatting)
30
+ - `<span>` - Inline formatting with CSS styles (bold, italic, underline, color)
31
+ - `<br>` - Line breaks
32
+ - `<div>` with bg/border - Becomes shape
33
+ - `<img>` - Images
34
+ - `class="placeholder"` - Reserved space for charts (returns `{ id, x, y, w, h }`)
35
+
36
+ ### Critical Text Rules
37
+
38
+ **ALL text MUST be inside `<p>`, `<h1>`-`<h6>`, `<ul>`, or `<ol>` tags:**
39
+ - βœ… Correct: `<div><p>Text here</p></div>`
40
+ - ❌ Wrong: `<div>Text here</div>` - **Text will NOT appear in PowerPoint**
41
+ - ❌ Wrong: `<span>Text</span>` - **Text will NOT appear in PowerPoint**
42
+
43
+ **NEVER use manual bullet symbols (β€’, -, *, etc.)** - Use `<ul>` or `<ol>` lists instead
44
+
45
+ **ONLY use web-safe fonts:**
46
+ - βœ… Web-safe: `Arial`, `Helvetica`, `Times New Roman`, `Georgia`, `Courier New`, `Verdana`, `Tahoma`, `Trebuchet MS`, `Impact`
47
+ - ❌ Wrong: `'Segoe UI'`, `'SF Pro'`, `'Roboto'`, custom fonts
48
+
49
+ ### Styling
50
+
51
+ - Use `display: flex` on body to prevent margin collapse
52
+ - Use `margin` for spacing (padding included in size)
53
+ - Flexbox works - positions calculated from rendered layout
54
+ - Use hex colors with `#` prefix in CSS
55
+
56
+ ### Shape Styling (DIV elements only)
57
+
58
+ **IMPORTANT: Backgrounds, borders, and shadows only work on `<div>` elements, NOT on text elements**
59
+
60
+ - **Backgrounds**: CSS `background` or `background-color` on `<div>` elements only
61
+ - **Borders**: CSS `border` on `<div>` elements converts to PowerPoint shape borders
62
+ - **Border radius**: CSS `border-radius` on `<div>` elements for rounded corners
63
+ - **Box shadows**: CSS `box-shadow` on `<div>` elements (outer shadows only)
64
+
65
+ ### Icons & Gradients
66
+
67
+ - **CRITICAL: Never use CSS gradients** - They don't convert to PowerPoint
68
+ - **ALWAYS create gradient/icon PNGs FIRST using Sharp, then reference in HTML**
69
+
70
+ **Rasterizing Icons with Sharp:**
71
+
72
+ ```javascript
73
+ const sharp = require('sharp');
74
+ const { FaHome } = require('react-icons/fa');
75
+
76
+ async function rasterizeIconPng(IconComponent, color, size, filename) {
77
+ const svgString = ReactDOMServer.renderToStaticMarkup(
78
+ React.createElement(IconComponent, { color: `#${color}`, size: size })
79
+ );
80
+ await sharp(Buffer.from(svgString)).png().toFile(filename);
81
+ return filename;
82
+ }
83
+ ```
84
+
85
+ ### Example HTML Slide
86
+
87
+ ```html
88
+ <!DOCTYPE html>
89
+ <html>
90
+ <head>
91
+ <style>
92
+ html { background: #ffffff; }
93
+ body {
94
+ width: 720pt; height: 405pt; margin: 0; padding: 0;
95
+ background: #f5f5f5; font-family: Arial, sans-serif;
96
+ display: flex;
97
+ }
98
+ .content { margin: 30pt; padding: 40pt; background: #ffffff; border-radius: 8pt; }
99
+ h1 { color: #2d3748; font-size: 32pt; }
100
+ </style>
101
+ </head>
102
+ <body>
103
+ <div class="content">
104
+ <h1>Title</h1>
105
+ <ul>
106
+ <li><b>Item:</b> Description</li>
107
+ </ul>
108
+ <div id="chart" class="placeholder" style="width: 350pt; height: 200pt;"></div>
109
+ </div>
110
+ </body>
111
+ </html>
112
+ ```
113
+
114
+ ## Using the html2pptx Library
115
+
116
+ ### Basic Usage
117
+
118
+ ```javascript
119
+ const pptxgen = require('pptxgenjs');
120
+ const html2pptx = require('./html2pptx');
121
+
122
+ const pptx = new pptxgen();
123
+ pptx.layout = 'LAYOUT_16x9';
124
+
125
+ const { slide, placeholders } = await html2pptx('slide1.html', pptx);
126
+
127
+ if (placeholders.length > 0) {
128
+ slide.addChart(pptx.charts.LINE, chartData, placeholders[0]);
129
+ }
130
+
131
+ await pptx.writeFile('output.pptx');
132
+ ```
133
+
134
+ ### API Reference
135
+
136
+ ```javascript
137
+ await html2pptx(htmlFile, pres, options)
138
+ // Returns: { slide, placeholders: [{ id, x, y, w, h }] }
139
+ ```
140
+
141
+ ## Using PptxGenJS
142
+
143
+ ### ⚠️ Critical Rules
144
+
145
+ **Colors - NEVER use `#` prefix:**
146
+ - βœ… Correct: `color: "FF0000"`, `fill: { color: "0066CC" }`
147
+ - ❌ Wrong: `color: "#FF0000"` (breaks document)
148
+
149
+ ### Adding Charts
150
+
151
+ ```javascript
152
+ slide.addChart(pptx.charts.BAR, [{
153
+ name: "Sales 2024",
154
+ labels: ["Q1", "Q2", "Q3", "Q4"],
155
+ values: [4500, 5500, 6200, 7100]
156
+ }], {
157
+ ...placeholders[0],
158
+ barDir: 'col',
159
+ showTitle: true,
160
+ title: 'Quarterly Sales',
161
+ showCatAxisTitle: true,
162
+ catAxisTitle: 'Quarter',
163
+ showValAxisTitle: true,
164
+ valAxisTitle: 'Sales ($000s)',
165
+ chartColors: ["4472C4"]
166
+ });
167
+ ```
168
+
169
+ ### Adding Tables
170
+
171
+ ```javascript
172
+ slide.addTable([
173
+ ["Header 1", "Header 2", "Header 3"],
174
+ ["Row 1, Col 1", "Row 1, Col 2", "Row 1, Col 3"]
175
+ ], {
176
+ x: 0.5, y: 1, w: 9, h: 3,
177
+ border: { pt: 1, color: "999999" },
178
+ fill: { color: "F1F1F1" }
179
+ });
180
+ ```
181
+
182
+ ### Adding Images
183
+
184
+ ```javascript
185
+ const aspectRatio = imgWidth / imgHeight;
186
+ const h = 3;
187
+ const w = h * aspectRatio;
188
+ const x = (10 - w) / 2;
189
+
190
+ slide.addImage({ path: "chart.png", x, y: 1.5, w, h });
191
+ ```