@hirokisakabe/pom 0.1.11 → 0.2.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/README.md CHANGED
@@ -9,19 +9,6 @@
9
9
  > [!NOTE]
10
10
  > The PPTX generation feature (`buildPptx`) only works in Node.js environments. However, if you only need the input schema, you can import it from `@hirokisakabe/pom/schema` for use in browser environments.
11
11
 
12
- ## Table of Contents
13
-
14
- - [Requirements](#requirements)
15
- - [Installation](#installation)
16
- - [Quick Start](#quick-start)
17
- - [Features](#features)
18
- - [Nodes](#nodes)
19
- - [Master Slide](#master-slide)
20
- - [Serverless Environments](#serverless-environments)
21
- - [LLM Integration](#llm-integration)
22
- - [Input Validation in Browser Environments](#input-validation-in-browser-environments)
23
- - [License](#license)
24
-
25
12
  ## Installation
26
13
 
27
14
  ```bash
@@ -70,573 +57,34 @@ await pptx.writeFile({ fileName: "presentation.pptx" });
70
57
  - **Master Slide**: Automatically insert common headers, footers, and page numbers across all pages
71
58
  - **AI Friendly**: Simple structure that makes it easy for LLMs to generate code
72
59
 
73
- ## Nodes
74
-
75
- ### Common Properties
76
-
77
- Layout attributes that all nodes can have.
78
-
79
- ```typescript
80
- {
81
- w?: number | "max" | `${number}%`;
82
- h?: number | "max" | `${number}%`;
83
- minW?: number;
84
- maxW?: number;
85
- minH?: number;
86
- maxH?: number;
87
- padding?: number;
88
- backgroundColor?: string;
89
- border?: {
90
- color?: string;
91
- width?: number;
92
- dashType?: "solid" | "dash" | "dashDot" | "lgDash" | "lgDashDot" | "lgDashDotDot" | "sysDash" | "sysDot";
93
- };
94
- }
95
- ```
96
-
97
- - `backgroundColor` applies a fill to the entire node (e.g., `"F8F9FA"`).
98
- - `border.width` is specified in px and can be combined with color and `dashType` to control the border.
99
-
100
- ### Node List
101
-
102
- #### 1. Text
103
-
104
- A node for displaying text.
105
-
106
- ```typescript
107
- {
108
- type: "text";
109
- text: string;
110
- fontPx?: number;
111
- color?: string;
112
- alignText?: "left" | "center" | "right";
113
- bold?: boolean;
114
- fontFamily?: string;
115
- lineSpacingMultiple?: number;
116
- bullet?: boolean | BulletOptions;
117
-
118
- // Common properties
119
- w?: number | "max" | `${number}%`;
120
- h?: number | "max" | `${number}%`;
121
- ...
122
- }
123
- ```
124
-
125
- - `color` specifies the text color as a hex color code (e.g., `"FF0000"`).
126
- - `bold` enables bold text.
127
- - `fontFamily` specifies the font family (default: `"Noto Sans JP"`).
128
- - `lineSpacingMultiple` specifies the line spacing multiplier (default: `1.3`).
129
- - `bullet` enables bullet points. Use `true` for default bullets, or an object for detailed settings.
130
-
131
- **BulletOptions:**
132
-
133
- ```typescript
134
- {
135
- type?: "bullet" | "number"; // "bullet": symbol, "number": numbered
136
- indent?: number; // Indent level
137
- numberType?: "alphaLcParenBoth" | "alphaLcParenR" | "alphaLcPeriod" |
138
- "alphaUcParenBoth" | "alphaUcParenR" | "alphaUcPeriod" |
139
- "arabicParenBoth" | "arabicParenR" | "arabicPeriod" | "arabicPlain" |
140
- "romanLcParenBoth" | "romanLcParenR" | "romanLcPeriod" |
141
- "romanUcParenBoth" | "romanUcParenR" | "romanUcPeriod";
142
- numberStartAt?: number; // Starting number
143
- }
144
- ```
145
-
146
- **Usage Examples:**
147
-
148
- ```typescript
149
- // Simple bullet list
150
- {
151
- type: "text",
152
- text: "Item 1\nItem 2\nItem 3",
153
- bullet: true,
154
- }
155
-
156
- // Numbered list
157
- {
158
- type: "text",
159
- text: "Step 1\nStep 2\nStep 3",
160
- bullet: { type: "number" },
161
- }
162
-
163
- // Lowercase alphabet (a. b. c.)
164
- {
165
- type: "text",
166
- text: "Item A\nItem B\nItem C",
167
- bullet: { type: "number", numberType: "alphaLcPeriod" },
168
- }
169
-
170
- // Numbered list starting from 5
171
- {
172
- type: "text",
173
- text: "Fifth\nSixth\nSeventh",
174
- bullet: { type: "number", numberStartAt: 5 },
175
- }
176
- ```
177
-
178
- #### 2. Image
179
-
180
- A node for displaying images.
181
-
182
- - If `w` and `h` are not specified, the actual image size is automatically used
183
- - If size is specified, the image is displayed at that size (aspect ratio is not preserved)
184
-
185
- ```typescript
186
- {
187
- type: "image";
188
- src: string; // Image path (local path, URL, or base64 data)
189
-
190
- // Common properties
191
- w?: number | "max" | `${number}%`;
192
- h?: number | "max" | `${number}%`;
193
- ...
194
- }
195
- ```
196
-
197
- #### 3. Table
198
-
199
- A node for drawing tables. Column widths and row heights are declared in px, with fine-grained control over cell decoration.
200
-
201
- ```typescript
202
- {
203
- type: "table";
204
- columns: { width?: number }[];
205
- rows: {
206
- height?: number;
207
- cells: {
208
- text: string;
209
- fontPx?: number;
210
- color?: string;
211
- bold?: boolean;
212
- alignText?: "left" | "center" | "right";
213
- backgroundColor?: string;
214
- }[];
215
- }[];
216
- defaultRowHeight?: number;
217
-
218
- // Common properties
219
- w?: number | "max" | `${number}%`;
220
- h?: number | "max" | `${number}%`;
221
- ...
222
- }
223
- ```
224
-
225
- - If `columns[].width` is omitted, columns are evenly distributed across the table width.
226
- - The sum of `columns` becomes the natural width of the table (can be overridden with `w` if needed).
227
- - If `rows` `height` is omitted, `defaultRowHeight` is applied (32px if unspecified).
228
- - Cell background and font decoration can be specified individually for each element in `cells`.
229
-
230
- #### 4. Shape
231
-
232
- A node for drawing shapes. Different representations are possible with or without text, supporting complex visual effects.
233
-
234
- ```typescript
235
- {
236
- type: "shape";
237
- shapeType: PptxGenJS.SHAPE_NAME; // e.g., "roundRect", "ellipse", "cloud", "star5"
238
- text?: string; // Text to display inside the shape (optional)
239
- fill?: {
240
- color?: string;
241
- transparency?: number;
242
- };
243
- line?: {
244
- color?: string;
245
- width?: number;
246
- dashType?: "solid" | "dash" | "dashDot" | "lgDash" | "lgDashDot" | "lgDashDotDot" | "sysDash" | "sysDot";
247
- };
248
- shadow?: {
249
- type: "outer" | "inner";
250
- opacity?: number;
251
- blur?: number;
252
- angle?: number;
253
- offset?: number;
254
- color?: string;
255
- };
256
- fontPx?: number;
257
- color?: string;
258
- alignText?: "left" | "center" | "right";
259
-
260
- // Common properties
261
- w?: number | "max" | `${number}%`;
262
- h?: number | "max" | `${number}%`;
263
- ...
264
- }
265
- ```
266
-
267
- **Common Shape Types:**
268
-
269
- - `roundRect`: Rounded rectangle (title boxes, category displays)
270
- - `ellipse`: Ellipse/circle (step numbers, badges)
271
- - `cloud`: Cloud shape (comments, key points)
272
- - `wedgeRectCallout`: Callout with arrow (annotations)
273
- - `cloudCallout`: Cloud callout (comments)
274
- - `star5`: 5-pointed star (emphasis, decoration)
275
- - `downArrow`: Down arrow (flow diagrams)
276
-
277
- #### 5. Box
278
-
279
- A generic container that wraps a single child element.
280
-
281
- - Only **one** child element
282
- - Used for grouping with padding or fixed size
283
-
284
- ```typescript
285
- {
286
- type: "box";
287
- children: POMNode;
288
-
289
- // Common properties
290
- w?: number | "max" | `${number}%`;
291
- h?: number | "max" | `${number}%`;
292
- ...
293
- }
294
- ```
295
-
296
- #### 6. VStack
297
-
298
- Arranges child elements **vertically**.
299
-
300
- ```typescript
301
- {
302
- type: "vstack";
303
- children: POMNode[];
304
- alignItems: "start" | "center" | "end" | "stretch";
305
- justifyContent: "start" | "center" | "end" | "spaceBetween";
306
- gap?: number;
307
-
308
- // Common properties
309
- w?: number | "max" | `${number}%`;
310
- h?: number | "max" | `${number}%`;
311
- ...
312
- }
313
- ```
314
-
315
- #### 7. HStack
316
-
317
- Arranges child elements **horizontally**.
318
-
319
- ```typescript
320
- {
321
- type: "hstack";
322
- children: POMNode[];
323
- alignItems: "start" | "center" | "end" | "stretch";
324
- justifyContent: "start" | "center" | "end" | "spaceBetween";
325
- gap?: number;
326
-
327
- // Common properties
328
- w?: number | "max" | `${number}%`;
329
- h?: number | "max" | `${number}%`;
330
- ...
331
- }
332
- ```
333
-
334
- #### 8. Chart
335
-
336
- A node for drawing charts. Supports bar charts, line charts, pie charts, area charts, doughnut charts, and radar charts.
337
-
338
- ```typescript
339
- {
340
- type: "chart";
341
- chartType: "bar" | "line" | "pie" | "area" | "doughnut" | "radar";
342
- data: {
343
- name?: string; // Series name
344
- labels: string[]; // Category labels
345
- values: number[]; // Values
346
- }[];
347
- showLegend?: boolean; // Show legend (default: false)
348
- showTitle?: boolean; // Show title (default: false)
349
- title?: string; // Title string
350
- chartColors?: string[]; // Data color array (hex color codes)
351
- radarStyle?: "standard" | "marker" | "filled"; // Radar-only: chart style
352
-
353
- // Common properties
354
- w?: number | "max" | `${number}%`;
355
- h?: number | "max" | `${number}%`;
356
- ...
357
- }
358
- ```
359
-
360
- **Usage Examples:**
361
-
362
- ```typescript
363
- // Bar chart
364
- {
365
- type: "chart",
366
- chartType: "bar",
367
- w: 600,
368
- h: 400,
369
- data: [
370
- {
371
- name: "Sales",
372
- labels: ["Jan", "Feb", "Mar", "Apr"],
373
- values: [100, 200, 150, 300],
374
- },
375
- {
376
- name: "Profit",
377
- labels: ["Jan", "Feb", "Mar", "Apr"],
378
- values: [30, 60, 45, 90],
379
- },
380
- ],
381
- showLegend: true,
382
- showTitle: true,
383
- title: "Monthly Sales & Profit",
384
- chartColors: ["0088CC", "00AA00"],
385
- }
386
-
387
- // Pie chart
388
- {
389
- type: "chart",
390
- chartType: "pie",
391
- w: 400,
392
- h: 300,
393
- data: [
394
- {
395
- name: "Market Share",
396
- labels: ["Product A", "Product B", "Product C", "Others"],
397
- values: [40, 30, 20, 10],
398
- },
399
- ],
400
- showLegend: true,
401
- chartColors: ["0088CC", "00AA00", "FF6600", "888888"],
402
- }
403
-
404
- // Radar chart
405
- {
406
- type: "chart",
407
- chartType: "radar",
408
- w: 400,
409
- h: 300,
410
- data: [
411
- {
412
- name: "Skill Assessment",
413
- labels: ["Technical", "Design", "PM", "Sales", "Support"],
414
- values: [80, 60, 70, 50, 90],
415
- },
416
- ],
417
- showLegend: true,
418
- radarStyle: "filled",
419
- chartColors: ["0088CC"],
420
- }
421
- ```
422
-
423
- ## Master Slide
424
-
425
- You can automatically insert common headers, footers, and page numbers across all pages.
426
-
427
- ### Basic Usage
428
-
429
- ```typescript
430
- import { buildPptx } from "@hirokisakabe/pom";
431
-
432
- const pptx = await buildPptx(
433
- [page1, page2, page3],
434
- { w: 1280, h: 720 },
435
- {
436
- master: {
437
- header: {
438
- type: "hstack",
439
- h: 40,
440
- padding: { left: 48, right: 48, top: 12, bottom: 0 },
441
- justifyContent: "spaceBetween",
442
- alignItems: "center",
443
- backgroundColor: "0F172A",
444
- children: [
445
- {
446
- type: "text",
447
- text: "Company Name",
448
- fontPx: 14,
449
- color: "FFFFFF",
450
- },
451
- {
452
- type: "text",
453
- text: "{{date}}",
454
- fontPx: 12,
455
- color: "E2E8F0",
456
- },
457
- ],
458
- },
459
- footer: {
460
- type: "hstack",
461
- h: 30,
462
- padding: { left: 48, right: 48, top: 0, bottom: 8 },
463
- justifyContent: "spaceBetween",
464
- alignItems: "center",
465
- children: [
466
- {
467
- type: "text",
468
- text: "Confidential",
469
- fontPx: 10,
470
- color: "1E293B",
471
- },
472
- {
473
- type: "text",
474
- text: "Page {{page}} / {{totalPages}}",
475
- fontPx: 10,
476
- color: "1E293B",
477
- alignText: "right",
478
- },
479
- ],
480
- },
481
- date: {
482
- format: "YYYY/MM/DD", // or "locale"
483
- },
484
- },
485
- },
486
- );
487
- ```
488
-
489
- ### Master Slide Options
490
-
491
- ```typescript
492
- type MasterSlideOptions = {
493
- header?: POMNode; // Header (any POMNode can be specified)
494
- footer?: POMNode; // Footer (any POMNode can be specified)
495
- pageNumber?: {
496
- position: "left" | "center" | "right"; // Page number position
497
- };
498
- date?: {
499
- format: "YYYY/MM/DD" | "locale"; // Date format
500
- };
501
- };
502
- ```
503
-
504
- ### Placeholders
505
-
506
- The following placeholders can be used in text within headers and footers:
507
-
508
- - `{{page}}`: Current page number
509
- - `{{totalPages}}`: Total number of pages
510
- - `{{date}}`: Date (in the format specified by `date.format`)
511
-
512
- ### Features
513
-
514
- - **Flexibility**: Headers and footers can use any POMNode (VStack, HStack, Box, etc.)
515
- - **Automatic Composition**: Headers and footers are automatically added to each page's content
516
- - **Dynamic Replacement**: Placeholders are automatically replaced per page
517
- - **Backward Compatibility**: The master option is optional and has no impact on existing code
518
-
519
- ## Serverless Environments
520
-
521
- pom uses the `canvas` package by default to measure text width and determine line break positions. However, in serverless environments like Vercel or AWS Lambda, Japanese fonts (such as Noto Sans JP) are not installed, which may cause text line breaks to be misaligned.
522
-
523
- To address this issue, you can specify the text measurement method using the `textMeasurement` option.
524
-
525
- ### textMeasurement Option
526
-
527
- ```typescript
528
- const pptx = await buildPptx(
529
- [slide],
530
- { w: 1280, h: 720 },
531
- {
532
- textMeasurement: "auto", // "canvas" | "fallback" | "auto"
533
- },
534
- );
535
- ```
536
-
537
- | Value | Description |
538
- | ------------ | -------------------------------------------------------------------------------------- |
539
- | `"canvas"` | Always use canvas for text width measurement (for environments with fonts installed) |
540
- | `"fallback"` | Always use fallback calculation (CJK characters = 1em, alphanumeric = 0.5em estimated) |
541
- | `"auto"` | Auto-detect font availability and fall back if unavailable (default) |
542
-
543
- ### Recommended Settings
544
-
545
- - **Local development / Docker**: Default (`"auto"`) works fine
546
- - **Serverless environments**: Default `"auto"` will automatically fall back
547
- - **Environments with fonts installed**: Explicitly specifying `"canvas"` enables more accurate measurement
548
-
549
- ## LLM Integration
550
-
551
- pom supports use cases where slides are created from JSON generated by LLMs (GPT-4o, Claude, etc.).
552
-
553
- ### LLM Specification Guide
554
-
555
- [`llm-guide.md`](./llm-guide.md) is a compact specification document for having LLMs generate JSON in pom format. Include it in your system prompt.
556
-
557
- **Contents:**
558
-
559
- - Node list and main properties
560
- - Standard settings (slide size, padding, gap, font size guidelines)
561
- - Pattern examples (basic structure, 2-column, table, shapes, charts, etc.)
562
- - Common mistakes and correct usage
563
-
564
- ### Input Schema
565
-
566
- Use `inputPomNodeSchema` to validate JSON generated by LLMs.
567
-
568
- ```typescript
569
- import { inputPomNodeSchema, buildPptx, InputPOMNode } from "@hirokisakabe/pom";
570
-
571
- // Validate JSON output from LLM
572
- const jsonFromLLM = `{
573
- "type": "vstack",
574
- "padding": 48,
575
- "gap": 24,
576
- "children": [
577
- { "type": "text", "text": "Title", "fontPx": 32, "bold": true },
578
- { "type": "text", "text": "Body text", "fontPx": 16 }
579
- ]
580
- }`;
581
-
582
- const parsed = JSON.parse(jsonFromLLM);
583
- const result = inputPomNodeSchema.safeParse(parsed);
584
-
585
- if (result.success) {
586
- // Validation successful - generate PPTX
587
- const pptx = await buildPptx([result.data], { w: 1280, h: 720 });
588
- await pptx.writeFile({ fileName: "output.pptx" });
589
- } else {
590
- // Validation failed - check error details
591
- console.error("Validation failed:", result.error.format());
592
- }
593
- ```
594
-
595
- ### Available Input Schemas
596
-
597
- | Schema | Description |
598
- | ------------------------------- | ------------------------------------------ |
599
- | `inputPomNodeSchema` | Main node schema (includes all node types) |
600
- | `inputTextNodeSchema` | For text nodes |
601
- | `inputImageNodeSchema` | For image nodes |
602
- | `inputTableNodeSchema` | For table nodes |
603
- | `inputShapeNodeSchema` | For shape nodes |
604
- | `inputChartNodeSchema` | For chart nodes |
605
- | `inputBoxNodeSchema` | For Box nodes |
606
- | `inputVStackNodeSchema` | For VStack nodes |
607
- | `inputHStackNodeSchema` | For HStack nodes |
608
- | `inputMasterSlideOptionsSchema` | For master slide settings |
609
-
610
- ### Input Validation in Browser Environments
611
-
612
- If you want to validate LLM output in browser environments (SPAs like React, Vue, Svelte), you can import schemas from `@hirokisakabe/pom/schema`. This subpath exports only schemas without Node.js dependencies, so it works in browsers.
613
-
614
- ```typescript
615
- // Available in browser environments
616
- import { inputPomNodeSchema } from "@hirokisakabe/pom/schema";
617
-
618
- // Validate response from LLM
619
- const result = inputPomNodeSchema.safeParse(llmResponse);
620
-
621
- if (result.success) {
622
- // Validation successful - send to server for PPTX generation
623
- await fetch("/api/generate-pptx", {
624
- method: "POST",
625
- headers: { "Content-Type": "application/json" },
626
- body: JSON.stringify(result.data),
627
- });
628
- } else {
629
- // Validation failed - show error to user
630
- console.error("Validation failed:", result.error.format());
631
- }
632
- ```
633
-
634
- **Difference between `@hirokisakabe/pom` and `@hirokisakabe/pom/schema`:**
635
-
636
- | Import Path | Environment | Included Features |
637
- | -------------------------- | --------------- | -------------------------------------------- |
638
- | `@hirokisakabe/pom` | Node.js | Everything (PPTX generation, schemas, types) |
639
- | `@hirokisakabe/pom/schema` | Browser-capable | Schemas and types only (no PPTX generation) |
60
+ ## Available Nodes
61
+
62
+ | Node | Description |
63
+ | ------------ | ---------------------------------------------- |
64
+ | text | Text with font styling and bullet points |
65
+ | image | Images from file path, URL, or base64 |
66
+ | table | Tables with customizable columns and rows |
67
+ | shape | PowerPoint shapes (roundRect, ellipse, etc.) |
68
+ | chart | Charts (bar, line, pie, area, doughnut, radar) |
69
+ | timeline | Timeline/roadmap visualizations |
70
+ | matrix | 2x2 positioning maps |
71
+ | tree | Organization charts and decision trees |
72
+ | flow | Flowcharts with nodes and edges |
73
+ | processArrow | Chevron-style process diagrams |
74
+ | box | Container for single child with padding |
75
+ | vstack | Vertical stack layout |
76
+ | hstack | Horizontal stack layout |
77
+
78
+ For detailed node documentation, see [Nodes Reference](./docs/nodes.md).
79
+
80
+ ## Documentation
81
+
82
+ | Document | Description |
83
+ | ----------------------------------------------- | --------------------------------------- |
84
+ | [Nodes Reference](./docs/nodes.md) | Complete reference for all node types |
85
+ | [Master Slide](./docs/master-slide.md) | Headers, footers, and page numbers |
86
+ | [Serverless Environments](./docs/serverless.md) | Text measurement options for serverless |
87
+ | [LLM Integration](./docs/llm-integration.md) | Guide for generating slides with AI/LLM |
640
88
 
641
89
  ## License
642
90
 
@@ -88,6 +88,11 @@ async function buildPomWithYogaTree(node, parentYoga, parentNode) {
88
88
  case "image":
89
89
  case "table":
90
90
  case "shape":
91
+ case "timeline":
92
+ case "matrix":
93
+ case "tree":
94
+ case "flow":
95
+ case "processArrow":
91
96
  // 子要素なし
92
97
  break;
93
98
  }
@@ -345,5 +350,12 @@ async function applyStyleToYogaNode(node, yn) {
345
350
  // テキストがない場合は、明示的にサイズが指定されていることを期待
346
351
  }
347
352
  break;
353
+ case "timeline":
354
+ case "matrix":
355
+ case "tree":
356
+ case "flow":
357
+ case "processArrow":
358
+ // 明示的にサイズが指定されていることを期待
359
+ break;
348
360
  }
349
361
  }