@hirokisakabe/pom 1.4.0 → 2.0.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.
Files changed (52) hide show
  1. package/README.md +22 -177
  2. package/dist/buildPptx.d.ts +2 -2
  3. package/dist/buildPptx.d.ts.map +1 -1
  4. package/dist/buildPptx.js +3 -1
  5. package/dist/calcYogaLayout/fontLoader.d.ts +0 -7
  6. package/dist/calcYogaLayout/fontLoader.d.ts.map +1 -1
  7. package/dist/calcYogaLayout/fontLoader.js +1 -1
  8. package/dist/calcYogaLayout/measureText.d.ts +0 -4
  9. package/dist/calcYogaLayout/measureText.d.ts.map +1 -1
  10. package/dist/calcYogaLayout/measureText.js +0 -6
  11. package/dist/index.d.ts +2 -5
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -4
  14. package/dist/inputSchema.d.ts +3 -438
  15. package/dist/inputSchema.d.ts.map +1 -1
  16. package/dist/inputSchema.js +6 -138
  17. package/dist/parseXml.d.ts +5 -1
  18. package/dist/parseXml.d.ts.map +1 -1
  19. package/dist/parseXml.js +502 -39
  20. package/dist/renderPptx/renderPptx.d.ts +1 -2
  21. package/dist/renderPptx/renderPptx.d.ts.map +1 -1
  22. package/dist/renderPptx/renderPptx.js +0 -2
  23. package/dist/renderPptx/textOptions.d.ts +0 -1
  24. package/dist/renderPptx/textOptions.d.ts.map +1 -1
  25. package/dist/renderPptx/textOptions.js +1 -1
  26. package/dist/renderPptx/types.d.ts +0 -2
  27. package/dist/renderPptx/types.d.ts.map +1 -1
  28. package/dist/table/utils.d.ts +0 -2
  29. package/dist/table/utils.d.ts.map +1 -1
  30. package/dist/table/utils.js +2 -2
  31. package/dist/types.d.ts +13 -121
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/types.js +21 -27
  34. package/package.json +3 -6
  35. package/dist/component.d.ts +0 -90
  36. package/dist/component.d.ts.map +0 -1
  37. package/dist/component.js +0 -123
  38. package/dist/renderPptx/nodes/box.d.ts +0 -2
  39. package/dist/renderPptx/nodes/box.d.ts.map +0 -1
  40. package/dist/renderPptx/nodes/box.js +0 -3
  41. package/dist/renderPptx/utils/index.d.ts +0 -6
  42. package/dist/renderPptx/utils/index.d.ts.map +0 -1
  43. package/dist/renderPptx/utils/index.js +0 -3
  44. package/dist/renderPptx/utils/shapeDrawing.d.ts +0 -27
  45. package/dist/renderPptx/utils/shapeDrawing.d.ts.map +0 -1
  46. package/dist/renderPptx/utils/shapeDrawing.js +0 -36
  47. package/dist/renderPptx/utils/textDrawing.d.ts +0 -25
  48. package/dist/renderPptx/utils/textDrawing.d.ts.map +0 -1
  49. package/dist/renderPptx/utils/textDrawing.js +0 -25
  50. package/dist/schema.d.ts +0 -23
  51. package/dist/schema.d.ts.map +0 -1
  52. package/dist/schema.js +0 -24
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # pom
2
2
 
3
- **pom (PowerPoint Object Model)** is a library for declaratively describing PowerPoint presentations (pptx) in TypeScript. It is designed for use cases where JSON in POM format generated by AI is converted into PowerPoint files.
3
+ **pom (PowerPoint Object Model)** is a library for declaratively describing PowerPoint presentations (pptx) in TypeScript. It is designed for use cases where XML in POM format generated by AI is converted into PowerPoint files.
4
+
5
+ > **[Try it online](https://pom-playground.vercel.app/)** — XML Playground
4
6
 
5
7
  ## Requirements
6
8
 
@@ -19,39 +21,23 @@ npm install @hirokisakabe/pom
19
21
  ## Quick Start
20
22
 
21
23
  ```typescript
22
- import { buildPptx, POMNode } from "@hirokisakabe/pom";
23
-
24
- const slide: POMNode = {
25
- type: "vstack",
26
- w: "100%",
27
- h: "max",
28
- padding: 48,
29
- gap: 24,
30
- alignItems: "start",
31
- children: [
32
- {
33
- type: "text",
34
- text: "Presentation Title",
35
- fontPx: 48,
36
- bold: true,
37
- },
38
- {
39
- type: "text",
40
- text: "Subtitle",
41
- fontPx: 24,
42
- color: "666666",
43
- },
44
- ],
45
- };
46
-
47
- const pptx = await buildPptx([slide], { w: 1280, h: 720 });
24
+ import { buildPptx } from "@hirokisakabe/pom";
25
+
26
+ const xml = `
27
+ <VStack w="100%" h="max" padding="48" gap="24" alignItems="start">
28
+ <Text fontPx="48" bold="true">Presentation Title</Text>
29
+ <Text fontPx="24" color="666666">Subtitle</Text>
30
+ </VStack>
31
+ `;
32
+
33
+ const pptx = await buildPptx(xml, { w: 1280, h: 720 });
48
34
  await pptx.writeFile({ fileName: "presentation.pptx" });
49
35
  ```
50
36
 
51
37
  ## Features
52
38
 
53
- - **Type-safe**: Strict type definitions with TypeScript
54
- - **Declarative**: Describe slides with JSON-like objects
39
+ - **Schema-validated**: XML input validated with Zod schemas at runtime
40
+ - **Declarative**: Describe slides with XML
55
41
  - **PowerPoint First**: Native support for Shape features
56
42
  - **Flexible Layout**: Automatic layout with VStack/HStack/Box
57
43
  - **Pixel Units**: Intuitive pixel-based sizing (internally converted to inches)
@@ -78,156 +64,15 @@ await pptx.writeFile({ fileName: "presentation.pptx" });
78
64
 
79
65
  For detailed node documentation, see [Nodes Reference](./docs/nodes.md).
80
66
 
81
- ## Components
82
-
83
- pom provides a `defineComponent` helper for creating reusable component templates. Components are simple functions that take props and return a `POMNode` — no runtime magic, no new node types.
84
-
85
- ```typescript
86
- import { defineComponent, mergeTheme, POMNode, Theme } from "@hirokisakabe/pom";
87
-
88
- // Define a reusable card component
89
- const SectionCard = defineComponent<{
90
- title: string;
91
- content: POMNode; // Slot: any POMNode
92
- theme?: Partial<Theme>; // Theme override
93
- }>((props) => {
94
- const t = mergeTheme(props.theme);
95
- return {
96
- type: "box",
97
- padding: t.spacing.md,
98
- backgroundColor: "FFFFFF",
99
- border: { color: t.colors.border, width: 1 },
100
- borderRadius: 8,
101
- children: {
102
- type: "vstack",
103
- gap: t.spacing.sm,
104
- children: [
105
- {
106
- type: "text",
107
- text: props.title,
108
- fontPx: t.fontPx.heading,
109
- bold: true,
110
- },
111
- props.content,
112
- ],
113
- },
114
- };
115
- });
116
-
117
- // Use the component
118
- const slide: POMNode = {
119
- type: "vstack",
120
- w: 1280,
121
- h: 720,
122
- padding: 48,
123
- gap: 24,
124
- children: [
125
- SectionCard({
126
- title: "Revenue",
127
- content: { type: "text", text: "$1,000,000" },
128
- }),
129
- SectionCard({
130
- title: "Custom Theme",
131
- content: { type: "text", text: "Styled card" },
132
- theme: { colors: { border: "CBD5E1" } },
133
- }),
134
- ],
135
- };
136
- ```
137
-
138
- Components support:
139
-
140
- - **Slots**: Pass `POMNode` or `POMNode[]` as props for content injection
141
- - **Theme**: Use `mergeTheme()` for colors, spacing, and font size overrides
142
- - **Nesting**: Components can call other components
143
- - **JSON / LLM**: Use `expandComponents()` to resolve component references in JSON
144
-
145
- ### Using Components with LLM-generated JSON
146
-
147
- When an LLM outputs JSON containing `{ type: "component", name: "...", props: {...} }`, use `expandComponentSlides()` to resolve them before building:
148
-
149
- ```typescript
150
- import {
151
- buildPptx,
152
- defineComponent,
153
- expandComponentSlides,
154
- } from "@hirokisakabe/pom";
155
-
156
- // Register components
157
- const SectionCard = defineComponent<{ title: string; content: unknown }>(
158
- (props) => ({
159
- type: "box",
160
- padding: 16,
161
- children: {
162
- type: "vstack",
163
- gap: 8,
164
- children: [
165
- { type: "text", text: props.title, bold: true },
166
- props.content,
167
- ],
168
- },
169
- }),
170
- );
171
-
172
- const registry = { SectionCard };
173
-
174
- // LLM output (JSON with component references)
175
- const llmOutput = [
176
- {
177
- type: "vstack",
178
- w: 1280,
179
- h: 720,
180
- children: [
181
- {
182
- type: "component",
183
- name: "SectionCard",
184
- props: { title: "KPI", content: { type: "text", text: "$1M" } },
185
- },
186
- ],
187
- },
188
- ];
189
-
190
- // Expand → Build
191
- const slides = expandComponentSlides(llmOutput, registry);
192
- const pptx = await buildPptx(slides, { w: 1280, h: 720 });
193
- ```
194
-
195
- ## XML Input
196
-
197
- `parseXml` allows you to describe slides in XML instead of JSON. XML tags (PascalCase) are mapped to POM node types, and attribute values are automatically type-coerced using Zod schemas.
198
-
199
- ```typescript
200
- import { parseXml, buildPptx } from "@hirokisakabe/pom";
201
-
202
- const xml = `
203
- <VStack gap="16" padding="32">
204
- <Text fontPx="32" bold="true">売上レポート</Text>
205
- <HStack gap="16">
206
- <Chart chartType="bar" w="400" h="300"
207
- data='[{ "name": "Q1", "labels": ["1月","2月","3月"], "values": [100,120,90] }]'
208
- />
209
- <Text fontPx="18" color="00AA00">前年比 +15%</Text>
210
- </HStack>
211
- </VStack>
212
- `;
213
-
214
- const nodes = parseXml(xml);
215
- const pptx = await buildPptx(nodes, { w: 1280, h: 720 });
216
- ```
217
-
218
- Unknown tags are treated as component nodes (`{ type: "component", name: tagName, props: {...} }`), which can be resolved with `expandComponents()`.
219
-
220
- For more details, see [LLM Integration - XML Format](./docs/llm-integration.md#xml-format).
221
-
222
67
  ## Documentation
223
68
 
224
- | Document | Description |
225
- | ----------------------------------------------- | --------------------------------------- |
226
- | [Nodes Reference](./docs/nodes.md) | Complete reference for all node types |
227
- | [Master Slide](./docs/master-slide.md) | Headers, footers, and page numbers |
228
- | [Serverless Environments](./docs/serverless.md) | Text measurement options for serverless |
229
- | [LLM Integration](./docs/llm-integration.md) | Guide for generating slides with AI/LLM |
230
- | [Components](./docs/nodes.md#components) | Reusable component templates |
69
+ | Document | Description |
70
+ | ------------------------------------------------ | --------------------------------------- |
71
+ | [Nodes Reference](./docs/nodes.md) | Complete reference for all node types |
72
+ | [Master Slide](./docs/master-slide.md) | Headers, footers, and page numbers |
73
+ | [Serverless Environments](./docs/serverless.md) | Text measurement options for serverless |
74
+ | [LLM Integration](./docs/llm-integration.md) | Compact XML reference for LLM prompts |
75
+ | [Playground](https://pom-playground.vercel.app/) | Try pom XML in the browser |
231
76
 
232
77
  ## License
233
78
 
@@ -1,7 +1,7 @@
1
1
  import { TextMeasurementMode } from "./calcYogaLayout/measureText.ts";
2
- import { POMNode, SlideMasterOptions } from "./types.ts";
2
+ import { SlideMasterOptions } from "./types.ts";
3
3
  export type { TextMeasurementMode };
4
- export declare function buildPptx(nodes: POMNode[], slideSize: {
4
+ export declare function buildPptx(xml: string, slideSize: {
5
5
  w: number;
6
6
  h: number;
7
7
  }, options?: {
@@ -1 +1 @@
1
- {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,mBAAmB,EACpB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,OAAO,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEzE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,SAAS,CAC7B,KAAK,EAAE,OAAO,EAAE,EAChB,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC,wCAoBF"}
1
+ {"version":3,"file":"buildPptx.d.ts","sourceRoot":"","sources":["../src/buildPptx.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,mBAAmB,EACpB,MAAM,iCAAiC,CAAC;AAIzC,OAAO,EAAkB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhE,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACnC,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,eAAe,CAAC,EAAE,mBAAmB,CAAC;CACvC,wCAqBF"}
package/dist/buildPptx.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { calcYogaLayout } from "./calcYogaLayout/calcYogaLayout.js";
2
2
  import { setTextMeasurementMode, } from "./calcYogaLayout/measureText.js";
3
+ import { parseXml } from "./parseXml.js";
3
4
  import { renderPptx } from "./renderPptx/renderPptx.js";
4
5
  import { toPositioned } from "./toPositioned/toPositioned.js";
5
- export async function buildPptx(nodes, slideSize, options) {
6
+ export async function buildPptx(xml, slideSize, options) {
6
7
  // テキスト計測モードを設定(デフォルトは auto)
7
8
  if (options?.textMeasurement) {
8
9
  setTextMeasurementMode(options.textMeasurement);
@@ -10,6 +11,7 @@ export async function buildPptx(nodes, slideSize, options) {
10
11
  else {
11
12
  setTextMeasurementMode("auto");
12
13
  }
14
+ const nodes = parseXml(xml);
13
15
  const positionedPages = [];
14
16
  for (const node of nodes) {
15
17
  await calcYogaLayout(node, slideSize);
@@ -2,13 +2,6 @@
2
2
  * opentype.js を使用したフォント読み込みモジュール
3
3
  * Node.js とブラウザ両方で動作する
4
4
  */
5
- import { Font } from "opentype.js";
6
- /**
7
- * フォントを取得する(キャッシュ付き)
8
- * @param weight フォントウェイト ("normal" or "bold")
9
- * @returns opentype.js の Font オブジェクト
10
- */
11
- export declare function getFont(weight: "normal" | "bold"): Font;
12
5
  /**
13
6
  * 指定したテキストの幅を計測する
14
7
  * @param text 計測するテキスト
@@ -1 +1 @@
1
- {"version":3,"file":"fontLoader.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/fontLoader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAiB,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AA6B7C;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAqBvD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,GAAG,MAAM,GACxB,MAAM,CAGR"}
1
+ {"version":3,"file":"fontLoader.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/fontLoader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2DH;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,QAAQ,GAAG,MAAM,GACxB,MAAM,CAGR"}
@@ -30,7 +30,7 @@ function base64ToArrayBuffer(base64) {
30
30
  * @param weight フォントウェイト ("normal" or "bold")
31
31
  * @returns opentype.js の Font オブジェクト
32
32
  */
33
- export function getFont(weight) {
33
+ function getFont(weight) {
34
34
  const cacheKey = weight;
35
35
  // キャッシュがあればそれを返す
36
36
  const cached = fontCache.get(cacheKey);
@@ -9,10 +9,6 @@ export type TextMeasurementMode = "opentype" | "fallback" | "auto";
9
9
  * テキスト計測モードを設定する
10
10
  */
11
11
  export declare function setTextMeasurementMode(mode: TextMeasurementMode): void;
12
- /**
13
- * 現在のテキスト計測モードを取得する
14
- */
15
- export declare function getTextMeasurementMode(): TextMeasurementMode;
16
12
  /**
17
13
  * テキストを折り返し付きでレイアウトし、そのサイズを測定する
18
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"measureText.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureText.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAoHnE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEtE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,mBAAmB,CAE5D;AAcD;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,cAAc,GACnB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA"}
1
+ {"version":3,"file":"measureText.d.ts","sourceRoot":"","sources":["../../src/calcYogaLayout/measureText.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAoHnE;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAEtE;AAcD;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,cAAc,GACnB;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAmBA"}
@@ -102,12 +102,6 @@ let currentMode = "auto";
102
102
  export function setTextMeasurementMode(mode) {
103
103
  currentMode = mode;
104
104
  }
105
- /**
106
- * 現在のテキスト計測モードを取得する
107
- */
108
- export function getTextMeasurementMode() {
109
- return currentMode;
110
- }
111
105
  /**
112
106
  * fontWeight を "normal" | "bold" に正規化する
113
107
  */
package/dist/index.d.ts CHANGED
@@ -1,8 +1,5 @@
1
- export * from "./types.ts";
2
- export * from "./inputSchema.ts";
3
1
  export { buildPptx } from "./buildPptx.ts";
4
2
  export type { TextMeasurementMode } from "./buildPptx.ts";
5
- export { defineComponent, defaultTheme, mergeTheme, expandComponents, expandComponentSlides, } from "./component.ts";
6
- export type { Theme, ComponentRegistry } from "./component.ts";
7
- export { parseXml } from "./parseXml.ts";
3
+ export { ParseXmlError } from "./parseXml.ts";
4
+ export type { SlideMasterOptions, SlideMasterBackground, SlideMasterMargin, MasterObject, MasterTextObject, MasterImageObject, MasterRectObject, MasterLineObject, SlideNumberOptions, } from "./types.ts";
8
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,eAAe,EACf,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,2 @@
1
- export * from "./types.js";
2
- export * from "./inputSchema.js";
3
1
  export { buildPptx } from "./buildPptx.js";
4
- export { defineComponent, defaultTheme, mergeTheme, expandComponents, expandComponentSlides, } from "./component.js";
5
- export { parseXml } from "./parseXml.js";
2
+ export { ParseXmlError } from "./parseXml.js";