@hellkite/pipkin 0.6.2 → 0.8.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 (64) hide show
  1. package/build/src/lib/replacement.d.ts +5 -3
  2. package/build/src/lib/replacement.js +6 -3
  3. package/build/src/lib/replacement.js.map +1 -1
  4. package/build/src/lib/template.d.ts +18 -26
  5. package/build/src/lib/template.js +197 -70
  6. package/build/src/lib/template.js.map +1 -1
  7. package/build/src/lib/types/containers.d.ts +14 -2
  8. package/build/src/lib/types/containers.js +2 -7
  9. package/build/src/lib/types/containers.js.map +1 -1
  10. package/build/src/lib/types/hypernode.d.ts +2 -0
  11. package/build/src/lib/types/hypernode.js +3 -0
  12. package/build/src/lib/types/hypernode.js.map +1 -0
  13. package/build/src/lib/types/image.d.ts +11 -5
  14. package/build/src/lib/types/image.js +2 -7
  15. package/build/src/lib/types/image.js.map +1 -1
  16. package/build/src/lib/types/index.d.ts +2 -0
  17. package/build/src/lib/types/index.js +2 -0
  18. package/build/src/lib/types/index.js.map +1 -1
  19. package/build/src/lib/types/layer.d.ts +6 -0
  20. package/build/src/lib/types/layer.js +7 -0
  21. package/build/src/lib/types/layer.js.map +1 -1
  22. package/build/src/lib/types/replacement.d.ts +2 -2
  23. package/build/src/lib/types/template.d.ts +13 -0
  24. package/build/src/lib/types/template.js +10 -0
  25. package/build/src/lib/types/template.js.map +1 -0
  26. package/build/src/lib/types/text.d.ts +9 -4
  27. package/build/src/lib/types/text.js +3 -10
  28. package/build/src/lib/types/text.js.map +1 -1
  29. package/build/src/lib/utils/container.js +10 -45
  30. package/build/src/lib/utils/container.js.map +1 -1
  31. package/build/src/lib/utils/htmlToImage.d.ts +2 -3
  32. package/build/src/lib/utils/htmlToImage.js.map +1 -1
  33. package/build/src/lib/utils/index.d.ts +1 -3
  34. package/build/src/lib/utils/index.js +1 -3
  35. package/build/src/lib/utils/index.js.map +1 -1
  36. package/build/src/lib/utils/placeBoundingBox.d.ts +2 -0
  37. package/build/src/lib/utils/placeBoundingBox.js +21 -0
  38. package/build/src/lib/utils/placeBoundingBox.js.map +1 -0
  39. package/build/src/lib/utils/placeImage.d.ts +7 -6
  40. package/build/src/lib/utils/placeImage.js +20 -20
  41. package/build/src/lib/utils/placeImage.js.map +1 -1
  42. package/build/src/lib/utils/placeText.d.ts +14 -0
  43. package/build/src/lib/utils/placeText.js +69 -0
  44. package/build/src/lib/utils/placeText.js.map +1 -0
  45. package/build/src/test.js +87 -15
  46. package/build/src/test.js.map +1 -1
  47. package/package.json +3 -1
  48. package/roadmap.md +3 -1
  49. package/src/lib/replacement.ts +6 -5
  50. package/src/lib/template.ts +336 -114
  51. package/src/lib/types/containers.ts +24 -8
  52. package/src/lib/types/hypernode.ts +4 -0
  53. package/src/lib/types/image.ts +20 -9
  54. package/src/lib/types/index.ts +2 -0
  55. package/src/lib/types/layer.ts +15 -0
  56. package/src/lib/types/replacement.ts +2 -2
  57. package/src/lib/types/template.ts +22 -0
  58. package/src/lib/types/text.ts +16 -12
  59. package/src/lib/utils/container.ts +13 -70
  60. package/src/lib/utils/htmlToImage.ts +3 -3
  61. package/src/lib/utils/index.ts +1 -3
  62. package/src/lib/utils/{drawBoundingBox.ts → placeBoundingBox.ts} +3 -9
  63. package/src/lib/utils/placeImage.ts +0 -62
  64. package/src/lib/utils/renderText.ts +0 -107
@@ -1,17 +1,24 @@
1
1
  import { JimpInstance } from 'jimp';
2
2
  import { ScaleMode } from './scale';
3
- import { LayerOptions } from './layer';
3
+ import { DEFAULT_LAYER_OPTIONS, LayerOptions } from './layer';
4
4
  import { RequiredDeep } from 'type-fest';
5
5
 
6
6
  export type ImageType = JimpInstance;
7
7
 
8
+ /**
9
+ * Static images -> `buffer`, `path`, `absolutePath`
10
+ * Dynamic images -> `key`, `pathFn`
11
+ */
8
12
  export type ImageRef<EntryType extends Record<string, string>> =
9
- | { buffer: Buffer }
10
- | { path: string }
11
- | { absolutePath: string }
13
+ | StaticImageRef
12
14
  | { key: keyof EntryType }
13
15
  | { pathFn: (entry: EntryType) => string };
14
16
 
17
+ export type StaticImageRef =
18
+ | { buffer: Buffer }
19
+ | { path: string }
20
+ | { absolutePath: string };
21
+
15
22
  export type ImageLayerOptions<EntryType extends Record<string, string>> =
16
23
  LayerOptions<EntryType> & {
17
24
  /**
@@ -28,14 +35,18 @@ export type ImageLayerOptions<EntryType extends Record<string, string>> =
28
35
  // processorFn?: (entry: EntryType, image: ImageType) => Promise<ImageType>;
29
36
  };
30
37
 
31
- export const DEFAULT_IMAGE_LAYER_OPTIONS: RequiredDeep<ImageLayerOptions<Record<string, string>>> = {
32
- justifyContent: 'center',
33
- alignItems: 'center',
38
+ export const DEFAULT_IMAGE_LAYER_OPTIONS: RequiredDeep<
39
+ ImageLayerOptions<Record<string, string>>
40
+ > = {
41
+ ...DEFAULT_LAYER_OPTIONS,
34
42
  scale: 'none',
35
- skip: false,
36
- assetsPath: ''
43
+ assetsPath: '',
37
44
  };
38
45
 
46
+ export type ImageLayerSpecificOptions<
47
+ EntryType extends Record<string, string>,
48
+ > = Omit<ImageLayerOptions<EntryType>, keyof LayerOptions<EntryType>>;
49
+
39
50
  export type Alignment = 'start' | 'center' | 'end';
40
51
 
41
52
  export const DEFAULT_IMAGE_ALIGNMENT: Alignment = 'center';
@@ -8,3 +8,5 @@ export * from './text';
8
8
  export * from './boundingBox';
9
9
  export * from './css';
10
10
  export * from './layer';
11
+ export * from './hypernode';
12
+ export * from './template';
@@ -1,3 +1,4 @@
1
+ import { RequiredDeep } from "type-fest";
1
2
  import { JustifyContent, AlignItems } from "./css";
2
3
 
3
4
  export type LayerOptions<EntryType extends Record<string, string>> = {
@@ -15,4 +16,18 @@ export type LayerOptions<EntryType extends Record<string, string>> = {
15
16
  * Decides if a layer should be rendered or not for a certain entry
16
17
  */
17
18
  skip?: boolean | ((entry: EntryType) => boolean);
19
+
20
+ /**
21
+ * Control if should render bounding box for this layer
22
+ */
23
+ renderBoundingBox?: boolean;
24
+ };
25
+
26
+ export const DEFAULT_LAYER_OPTIONS: RequiredDeep<
27
+ LayerOptions<Record<string, string>>
28
+ > = {
29
+ justifyContent: 'center',
30
+ alignItems: 'center',
31
+ skip: false,
32
+ renderBoundingBox: false,
18
33
  };
@@ -1,3 +1,3 @@
1
- import { ImageType } from './image';
1
+ import { StaticImageRef } from './image';
2
2
 
3
- export type ReplacementMap = Record<string, ImageType>;
3
+ export type ReplacementMap = Record<string, StaticImageRef>;
@@ -0,0 +1,22 @@
1
+ import { rgbaToInt } from "jimp";
2
+
3
+ type RequiredTemplateOptions = {
4
+ height: number;
5
+ width: number;
6
+ color: number;
7
+ };
8
+
9
+ type OptionalTemplateOptions = Partial<{
10
+ defaultFontFamily: string;
11
+ defaultAssetsPath: string;
12
+ // TODO: defaultOutputDirectoryPath
13
+ debug: boolean;
14
+ }>;
15
+
16
+ export type TemplateOptions = RequiredTemplateOptions & OptionalTemplateOptions;
17
+
18
+ export const DEFAULT_TEMPLATE_OPTIONS: RequiredTemplateOptions = {
19
+ height: 1050,
20
+ width: 750,
21
+ color: rgbaToInt(255, 255, 255, 255),
22
+ };
@@ -1,10 +1,14 @@
1
1
  import type { RequiredDeep } from 'type-fest';
2
- import { LayerOptions } from './layer';
3
- import { ReplacementMap } from './replacement';
2
+ import { DEFAULT_LAYER_OPTIONS, LayerOptions } from './layer';
3
+ import { Replacement } from '../replacement';
4
4
 
5
+ /**
6
+ * Static text -> `text`
7
+ * Dynamic text -> `key`, `textFn`
8
+ */
5
9
  export type TextRef<EntryType> =
6
- | { key: string }
7
10
  | { text: string }
11
+ | { key: string }
8
12
  | { textFn: (entry: EntryType) => string };
9
13
 
10
14
  export type FontOptions = {
@@ -38,30 +42,30 @@ export type TextLayerOptions<EntryType extends Record<string, string>> =
38
42
  color?: string;
39
43
  };
40
44
 
45
+ replacementFn?: (replace: Replacement) => Replacement;
46
+
41
47
  // TODO: processor fn
42
- replacement?: ReplacementMap;
43
48
  };
44
49
 
45
- export const DEFAULT_FONT: Required<
46
- FontOptions
47
- > = {
50
+ export const DEFAULT_FONT: Required<FontOptions> = {
48
51
  size: 28,
49
52
  family: 'Arial',
50
53
  bold: false,
51
54
  italic: false,
52
55
  };
53
56
 
57
+ export type TextLayerSpecificOptions<EntryType extends Record<string, string>> =
58
+ Omit<TextLayerOptions<EntryType>, keyof LayerOptions<EntryType>>;
59
+
54
60
  export const DEFAULT_TEXT_LAYER_OPTIONS: RequiredDeep<
55
61
  TextLayerOptions<Record<string, string>>
56
62
  > = {
57
- justifyContent: 'center',
58
- alignItems: 'center',
63
+ ...DEFAULT_LAYER_OPTIONS,
59
64
  font: DEFAULT_FONT,
60
65
  color: 'black',
61
- replacement: {},
62
- skip: false,
66
+ replacementFn: replacement => replacement,
63
67
  border: {
64
68
  width: 0,
65
69
  color: 'black',
66
- }
70
+ },
67
71
  };
@@ -2,28 +2,25 @@ import { h } from 'virtual-dom';
2
2
  import {
3
3
  DEFAULT_DIRECTION_CONTAINER_OPTIONS,
4
4
  DirectionContainerOptions,
5
- ImageType,
6
5
  PackingFn,
7
6
  BoundingBox,
8
7
  SCALE_MODE_TO_OBJECT_FIT,
9
- Size,
10
8
  GridContainerOptions,
11
9
  DEFAULT_GRID_CONTAINER_OPTIONS,
10
+ HyperNode,
12
11
  } from '../types';
13
12
  import { boundingBoxToPx, toPx } from './toPx';
14
13
  import merge from 'lodash.merge';
15
- import { htmlToImage } from './htmlToImage';
16
14
  import chunk from 'lodash.chunk';
17
15
 
18
16
  export const vboxPackingFn =
19
17
  <EntryType extends Record<string, string>>(
20
18
  options?: DirectionContainerOptions<EntryType>,
21
19
  ): PackingFn =>
22
- (box: BoundingBox, background: ImageType, images: Array<ImageType>) =>
20
+ (box: BoundingBox, elements: Array<HyperNode>) =>
23
21
  directionalPackingFn({
24
22
  isVertical: true,
25
- backgroundSize: background,
26
- images,
23
+ elements,
27
24
  box,
28
25
  options,
29
26
  });
@@ -32,11 +29,10 @@ export const hboxPackingFn =
32
29
  <EntryType extends Record<string, string>>(
33
30
  options?: DirectionContainerOptions<EntryType>,
34
31
  ): PackingFn =>
35
- (box: BoundingBox, background: ImageType, images: Array<ImageType>) =>
32
+ (box: BoundingBox, elements: Array<HyperNode>) =>
36
33
  directionalPackingFn({
37
34
  isVertical: false,
38
- backgroundSize: background,
39
- images,
35
+ elements,
40
36
  box,
41
37
  options,
42
38
  });
@@ -45,41 +41,15 @@ export const gridPackingFn =
45
41
  <EntryType extends Record<string, string>>(
46
42
  options?: GridContainerOptions<EntryType>,
47
43
  ): PackingFn =>
48
- async (
49
- box: BoundingBox,
50
- background: ImageType,
51
- images: Array<ImageType>,
52
- ) => {
44
+ async (box: BoundingBox, elements: Array<HyperNode>) => {
53
45
  const mergedOptions = merge(
54
46
  {},
55
47
  DEFAULT_GRID_CONTAINER_OPTIONS,
56
48
  options,
57
49
  );
58
- const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
59
-
60
- const children = await Promise.all(
61
- images.map(async image => {
62
- const imageBase64 = await image.getBase64('image/png');
63
- return h(
64
- 'img',
65
- {
66
- style: {
67
- objectFit,
68
- flex: '1 1 auto',
69
- minWidth: 0,
70
- minHeight: 0,
71
- maxWidth: '100%',
72
- maxHeight: '100%',
73
- },
74
- src: imageBase64,
75
- },
76
- [],
77
- );
78
- }),
79
- );
80
50
 
81
51
  const items = [];
82
- for (const subset of chunk(children)) {
52
+ for (const subset of chunk(elements)) {
83
53
  items.push(
84
54
  h(
85
55
  'div',
@@ -98,7 +68,7 @@ export const gridPackingFn =
98
68
  ),
99
69
  );
100
70
  }
101
- const document = h(
71
+ return h(
102
72
  'div',
103
73
  {
104
74
  style: {
@@ -112,51 +82,26 @@ export const gridPackingFn =
112
82
  },
113
83
  items,
114
84
  );
115
-
116
- return htmlToImage(document, background);
117
85
  };
118
86
 
119
87
  const directionalPackingFn = async <EntryType extends Record<string, string>>({
120
88
  isVertical,
121
- backgroundSize: background,
122
- images,
89
+ elements,
123
90
  box,
124
91
  options,
125
92
  }: {
126
93
  isVertical: boolean;
127
- backgroundSize: Size;
128
- images: Array<ImageType>;
94
+ elements: Array<HyperNode>;
129
95
  box: BoundingBox;
130
96
  options?: DirectionContainerOptions<EntryType>;
131
- }): Promise<ImageType> => {
97
+ }): Promise<HyperNode> => {
132
98
  const mergedOptions = merge(
133
99
  {},
134
100
  DEFAULT_DIRECTION_CONTAINER_OPTIONS,
135
101
  options,
136
102
  );
137
- const objectFit = SCALE_MODE_TO_OBJECT_FIT[mergedOptions.scale];
138
103
 
139
- const children = await Promise.all(
140
- images.map(async image => {
141
- const imageBase64 = await image.getBase64('image/png');
142
- return h(
143
- 'img',
144
- {
145
- style: {
146
- objectFit,
147
- flex: '1 1 auto',
148
- minWidth: 0,
149
- minHeight: 0,
150
- maxWidth: '100%',
151
- maxHeight: '100%',
152
- },
153
- src: imageBase64,
154
- },
155
- [],
156
- );
157
- }),
158
- );
159
- const document = h(
104
+ return h(
160
105
  'div',
161
106
  {
162
107
  style: {
@@ -172,8 +117,6 @@ const directionalPackingFn = async <EntryType extends Record<string, string>>({
172
117
  ...boundingBoxToPx(box),
173
118
  },
174
119
  },
175
- children,
120
+ elements,
176
121
  );
177
-
178
- return htmlToImage(document, background);
179
122
  };
@@ -1,10 +1,10 @@
1
- import { create as createElement, VNode } from 'virtual-dom';
1
+ import { create as createElement } from 'virtual-dom';
2
2
  import nodeHtmlToImage from 'node-html-to-image';
3
3
  import { Jimp } from 'jimp';
4
- import { ImageType, Size } from '../types';
4
+ import { HyperNode, ImageType, Size } from '../types';
5
5
 
6
6
  export const htmlToImage = async (
7
- document: VNode,
7
+ document: HyperNode,
8
8
  backgroundSize: Size,
9
9
  ): Promise<ImageType> => {
10
10
  const html = createElement(document).toString();
@@ -1,6 +1,4 @@
1
1
  export * from './container';
2
- export * from './drawBoundingBox';
3
- export * from './placeImage';
4
- export * from './renderText';
2
+ export * from './placeBoundingBox';
5
3
  export * from './htmlToImage';
6
4
  export * from './toPx';
@@ -1,13 +1,9 @@
1
1
  import { h } from 'virtual-dom';
2
- import { htmlToImage } from './htmlToImage';
3
2
  import { boundingBoxToPx } from './toPx';
4
- import { ImageType, Size, BoundingBox } from '../types';
3
+ import { BoundingBox, HyperNode } from '../types';
5
4
 
6
- export const drawBoundingBox = async (
7
- box: BoundingBox,
8
- imageSize: Size,
9
- ): Promise<ImageType> => {
10
- const document = h(
5
+ export const placeBoundingBox = async (box: BoundingBox): Promise<HyperNode> => {
6
+ return h(
11
7
  'div',
12
8
  {
13
9
  style: {
@@ -20,6 +16,4 @@ export const drawBoundingBox = async (
20
16
  },
21
17
  [],
22
18
  );
23
-
24
- return htmlToImage(document, imageSize);
25
19
  };
@@ -1,62 +0,0 @@
1
- import { h } from 'virtual-dom';
2
- import { boundingBoxToPx } from './toPx';
3
- import {
4
- ImageLayerOptions,
5
- ImageType,
6
- BoundingBox,
7
- SCALE_MODE_TO_OBJECT_FIT,
8
- } from '../types';
9
- import { htmlToImage } from './htmlToImage';
10
- import { RequiredDeep } from 'type-fest';
11
-
12
- type PlaceImageProps<EntryType extends Record<string, string>> = {
13
- image: ImageType;
14
- box: BoundingBox;
15
- backgroundSize: { width: number; height: number };
16
- options: RequiredDeep<ImageLayerOptions<EntryType>>;
17
- };
18
-
19
- export const placeImage = async <EntryType extends Record<string, string>>({
20
- image,
21
- box,
22
- backgroundSize,
23
- options,
24
- }: PlaceImageProps<EntryType>): Promise<ImageType> => {
25
- const imageBase64 = await image.getBase64('image/png');
26
- const objectFit = SCALE_MODE_TO_OBJECT_FIT[options.scale];
27
-
28
- const document = h(
29
- 'div',
30
- {
31
- style: {
32
- display: 'flex',
33
- position: 'absolute',
34
- scale: 1,
35
-
36
- justifyContent: options.justifyContent,
37
- alignItems: options.alignItems,
38
-
39
- ...boundingBoxToPx(box),
40
- },
41
- },
42
- [
43
- h(
44
- 'img',
45
- {
46
- style: {
47
- objectFit,
48
- flex: '1 1 auto',
49
- minWidth: 0,
50
- minHeight: 0,
51
- maxWidth: '100%',
52
- maxHeight: '100%',
53
- },
54
- src: imageBase64,
55
- },
56
- [],
57
- ),
58
- ],
59
- );
60
-
61
- return htmlToImage(document, backgroundSize);
62
- }
@@ -1,107 +0,0 @@
1
- import { h, VNode } from 'virtual-dom';
2
- import { ImageType, BoundingBox, Size, TextLayerOptions } from '../types';
3
- import { boundingBoxToPx, toPx } from './toPx';
4
- import { htmlToImage } from './htmlToImage';
5
- import { RequiredDeep } from 'type-fest';
6
-
7
- export const renderText = async <EntryType extends Record<string, string>>(
8
- text: string,
9
- box: BoundingBox,
10
- backgroundSize: Size,
11
- options: RequiredDeep<TextLayerOptions<EntryType>>,
12
- fonts: Record<string, string>,
13
- ): Promise<ImageType> => {
14
- let textChildren: Array<string | VNode> = [text];
15
- for (const [word, image] of Object.entries(options.replacement)) {
16
- const regex = new RegExp(word, 'gi');
17
- const imageBase64 = await image.getBase64('image/png');
18
-
19
- let tmpChildren: Array<string | VNode> = [];
20
- for (const textSegment of textChildren) {
21
- if (typeof textSegment !== 'string') {
22
- continue;
23
- }
24
-
25
- const parts = (textSegment as string).split(regex);
26
- for (let index = 0; index < parts.length; index++) {
27
- if (index > 0) {
28
- tmpChildren.push(
29
- h(
30
- 'img',
31
- {
32
- style: {
33
- display: 'inline',
34
- verticalAlign: 'middle',
35
- height: toPx(options.font.size),
36
- width: 'auto',
37
- },
38
- src: imageBase64,
39
- },
40
- [],
41
- ),
42
- );
43
- }
44
- tmpChildren.push(parts[index]);
45
- }
46
- }
47
-
48
- textChildren = tmpChildren;
49
- }
50
-
51
- const content = h(
52
- 'div',
53
- {
54
- style: {
55
- display: 'flex',
56
- overflow: 'visible',
57
- position: 'absolute',
58
-
59
- justifyContent: options.justifyContent,
60
- alignItems: options.alignItems,
61
-
62
- ...boundingBoxToPx(box),
63
- },
64
- },
65
- [
66
- h(
67
- 'div',
68
- {
69
- style: {
70
- overflow: 'visible',
71
- overflowWrap: 'word-wrap',
72
- whiteSpace: 'normal',
73
-
74
- color: options.color,
75
- fontFamily: options.font.family,
76
- fontSize: options.font.size,
77
- fontStyle: options.font.italic ? 'italic' : undefined,
78
- fontWeight: options.font.bold ? 'bold' : undefined,
79
-
80
- '-webkit-text-stroke': `${options.border.width}px ${options.border.color}`
81
- },
82
- },
83
- textChildren,
84
- ),
85
- ],
86
- );
87
-
88
- const document = h('html', [
89
- h('head', [
90
- h(
91
- 'style',
92
- Object.entries(fonts)
93
- .map(
94
- ([name, data]) =>
95
- `@font-face {
96
- font-family: '${name}';
97
- src: url(data:font/ttf;base64,${data}) format('truetype');
98
- }`,
99
- )
100
- .join('\n'),
101
- ),
102
- ]),
103
- h('body', [content]),
104
- ]);
105
-
106
- return htmlToImage(document, backgroundSize);
107
- };