@hirokisakabe/pom 7.4.0 → 8.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.
package/README.md CHANGED
@@ -58,16 +58,20 @@ npm install @hirokisakabe/pom
58
58
  import { buildPptx } from "@hirokisakabe/pom";
59
59
 
60
60
  const xml = `
61
- <VStack w="100%" h="max" padding="48" gap="24" alignItems="start">
62
- <Text fontSize="48" bold="true">Presentation Title</Text>
63
- <Text fontSize="24" color="666666">Subtitle</Text>
64
- </VStack>
61
+ <Slide>
62
+ <VStack w="100%" h="max" padding="48" gap="24" alignItems="start">
63
+ <Text fontSize="48" bold="true">Presentation Title</Text>
64
+ <Text fontSize="24" color="666666">Subtitle</Text>
65
+ </VStack>
66
+ </Slide>
65
67
  `;
66
68
 
67
69
  const { pptx } = await buildPptx(xml, { w: 1280, h: 720 });
68
70
  await pptx.writeFile({ fileName: "presentation.pptx" });
69
71
  ```
70
72
 
73
+ Each slide must be wrapped in a `<Slide>` element. To produce multiple slides, list multiple `<Slide>` elements at the top level.
74
+
71
75
  ## Available Nodes
72
76
 
73
77
  | Node | Description |
@@ -7,6 +7,10 @@ export declare const TAG_TO_TYPE: Record<string, string>;
7
7
  /**
8
8
  * XML 文字列を POMNode 配列に変換する。
9
9
  *
10
+ * 最上位は `<Slide>` 要素のみが許容される。各 `<Slide>` が 1 つのスライドに
11
+ * 対応し、その子要素がスライドのルート POMNode となる。子要素が複数ある場合は
12
+ * 暗黙的に VStack でラップされる。
13
+ *
10
14
  * XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して
11
15
  * 適切な型(number, boolean, array, object)に変換される。
12
16
  * 未知のタグ名が指定された場合はエラーがスローされる。
@@ -16,9 +20,11 @@ export declare const TAG_TO_TYPE: Record<string, string>;
16
20
  * import { parseXml, buildPptx } from "@hirokisakabe/pom";
17
21
  *
18
22
  * const xml = `
19
- * <VStack gap="16" padding="32">
20
- * <Text fontSize="32" bold="true">売上レポート</Text>
21
- * </VStack>
23
+ * <Slide>
24
+ * <VStack gap="16" padding="32">
25
+ * <Text fontSize="32" bold="true">売上レポート</Text>
26
+ * </VStack>
27
+ * </Slide>
22
28
  * `;
23
29
  *
24
30
  * const nodes = parseXml(xml);
@@ -1 +1 @@
1
- {"version":3,"file":"parseXml.d.ts","sourceRoot":"","sources":["../../src/parseXml/parseXml.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,OAAO,EAgBb,MAAM,aAAa,CAAC;AAYrB,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,MAAM,EAAE,MAAM,EAAE,CAAC;gBACrB,MAAM,EAAE,MAAM,EAAE;CAM7B;AAGD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoB9C,CAAC;AA6pCF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAiCrD"}
1
+ {"version":3,"file":"parseXml.d.ts","sourceRoot":"","sources":["../../src/parseXml/parseXml.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,OAAO,EAgBb,MAAM,aAAa,CAAC;AAYrB,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,MAAM,EAAE,MAAM,EAAE,CAAC;gBACrB,MAAM,EAAE,MAAM,EAAE;CAM7B;AAGD,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoB9C,CAAC;AA6pCF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CA6DrD"}
@@ -867,6 +867,10 @@ function convertPomNode(nodeType, tagName, attrs, childElements, textContent, er
867
867
  /**
868
868
  * XML 文字列を POMNode 配列に変換する。
869
869
  *
870
+ * 最上位は `<Slide>` 要素のみが許容される。各 `<Slide>` が 1 つのスライドに
871
+ * 対応し、その子要素がスライドのルート POMNode となる。子要素が複数ある場合は
872
+ * 暗黙的に VStack でラップされる。
873
+ *
870
874
  * XML タグは POM ノードタイプにマッピングされ、属性値は Zod スキーマを参照して
871
875
  * 適切な型(number, boolean, array, object)に変換される。
872
876
  * 未知のタグ名が指定された場合はエラーがスローされる。
@@ -876,9 +880,11 @@ function convertPomNode(nodeType, tagName, attrs, childElements, textContent, er
876
880
  * import { parseXml, buildPptx } from "@hirokisakabe/pom";
877
881
  *
878
882
  * const xml = `
879
- * <VStack gap="16" padding="32">
880
- * <Text fontSize="32" bold="true">売上レポート</Text>
881
- * </VStack>
883
+ * <Slide>
884
+ * <VStack gap="16" padding="32">
885
+ * <Text fontSize="32" bold="true">売上レポート</Text>
886
+ * </VStack>
887
+ * </Slide>
882
888
  * `;
883
889
  *
884
890
  * const nodes = parseXml(xml);
@@ -903,10 +909,37 @@ export function parseXml(xmlString) {
903
909
  const rootElement = parsed[0];
904
910
  const rootChildren = (rootElement["__root__"] ?? []);
905
911
  const errors = [];
906
- const nodes = rootChildren
907
- .filter((child) => !isTextNode(child))
908
- .map((child) => convertElement(child, errors))
909
- .filter((child) => child !== null);
912
+ const slideElements = rootChildren.filter((child) => !isTextNode(child));
913
+ const nodes = [];
914
+ for (const slideEl of slideElements) {
915
+ const tagName = getTagName(slideEl);
916
+ if (tagName !== "Slide") {
917
+ errors.push(`Top-level element must be <Slide>, but got <${tagName}>. Wrap your slide content in <Slide>...</Slide>.`);
918
+ continue;
919
+ }
920
+ if (Object.keys(getAttributes(slideEl)).length > 0) {
921
+ errors.push(`<Slide>: Attributes are not supported`);
922
+ }
923
+ const slideChildren = getChildElements(slideEl);
924
+ if (slideChildren.length === 0) {
925
+ errors.push(`<Slide> must contain at least one child element`);
926
+ continue;
927
+ }
928
+ const converted = slideChildren
929
+ .map((child) => convertElement(child, errors))
930
+ .filter((c) => c !== null);
931
+ if (converted.length === 0)
932
+ continue;
933
+ if (converted.length === 1) {
934
+ nodes.push(converted[0]);
935
+ }
936
+ else {
937
+ nodes.push({
938
+ type: "vstack",
939
+ children: converted,
940
+ });
941
+ }
942
+ }
910
943
  if (errors.length > 0) {
911
944
  throw new ParseXmlError(errors);
912
945
  }
@@ -1 +1 @@
1
- {"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/layer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,YAAY,EAAE,cA8D1B,CAAC"}
1
+ {"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/registry/definitions/layer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,eAAO,MAAM,YAAY,EAAE,cA8D1B,CAAC"}
@@ -44,7 +44,7 @@ export const layerNodeDef = {
44
44
  }
45
45
  const adjustedParentX = absoluteX + childX - childLayout.left;
46
46
  const adjustedParentY = absoluteY + childY - childLayout.top;
47
- return (await toPositioned(child, ctx, map, adjustedParentX, adjustedParentY));
47
+ return await toPositioned(child, ctx, map, adjustedParentX, adjustedParentY);
48
48
  })),
49
49
  };
50
50
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hirokisakabe/pom",
3
- "version": "7.4.0",
3
+ "version": "8.0.0",
4
4
  "description": "AI-friendly PowerPoint generation with a Flexbox layout engine.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,18 +45,18 @@
45
45
  "access": "public"
46
46
  },
47
47
  "devDependencies": {
48
- "@size-limit/file": "^12.0.1",
49
- "@types/node": "^25.5.2",
48
+ "@size-limit/file": "^12.1.0",
49
+ "@types/node": "^25.6.0",
50
50
  "@types/opentype.js": "^1.3.8",
51
51
  "@types/pngjs": "6.0.5",
52
- "@vitest/coverage-v8": "^4.1.2",
53
- "@vitest/ui": "^4.1.2",
54
- "lucide-static": "^1.7.0",
52
+ "@vitest/coverage-v8": "^4.1.5",
53
+ "@vitest/ui": "^4.1.5",
54
+ "lucide-static": "^1.11.0",
55
55
  "pixelmatch": "7.1.0",
56
56
  "pngjs": "7.0.0",
57
- "size-limit": "^12.0.1",
57
+ "size-limit": "^12.1.0",
58
58
  "tsx": "4.21.0",
59
- "typescript-eslint": "^8.58.0"
59
+ "typescript-eslint": "^8.59.0"
60
60
  },
61
61
  "dependencies": {
62
62
  "@resvg/resvg-wasm": "^2.6.2",