@meonode/canvas 1.0.0-beta.3 → 1.0.0-beta.5

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 (48) hide show
  1. package/Readme.md +1 -1
  2. package/dist/cjs/canvas/canvas.helper.d.ts +9 -11
  3. package/dist/cjs/canvas/canvas.helper.d.ts.map +1 -1
  4. package/dist/cjs/canvas/canvas.helper.js +47 -48
  5. package/dist/cjs/canvas/canvas.helper.js.map +1 -1
  6. package/dist/cjs/canvas/canvas.type.d.ts +31 -31
  7. package/dist/cjs/canvas/canvas.type.d.ts.map +1 -1
  8. package/dist/cjs/canvas/grid.canvas.util.d.ts +4 -4
  9. package/dist/cjs/canvas/grid.canvas.util.d.ts.map +1 -1
  10. package/dist/cjs/canvas/grid.canvas.util.js +21 -4
  11. package/dist/cjs/canvas/grid.canvas.util.js.map +1 -1
  12. package/dist/cjs/canvas/image.canvas.util.d.ts +0 -1
  13. package/dist/cjs/canvas/image.canvas.util.d.ts.map +1 -1
  14. package/dist/cjs/canvas/image.canvas.util.js +72 -72
  15. package/dist/cjs/canvas/image.canvas.util.js.map +1 -1
  16. package/dist/cjs/canvas/layout.canvas.util.d.ts +16 -17
  17. package/dist/cjs/canvas/layout.canvas.util.d.ts.map +1 -1
  18. package/dist/cjs/canvas/layout.canvas.util.js +17 -24
  19. package/dist/cjs/canvas/layout.canvas.util.js.map +1 -1
  20. package/dist/cjs/canvas/root.canvas.util.d.ts +4 -2
  21. package/dist/cjs/canvas/root.canvas.util.d.ts.map +1 -1
  22. package/dist/cjs/canvas/root.canvas.util.js +7 -3
  23. package/dist/cjs/canvas/root.canvas.util.js.map +1 -1
  24. package/dist/cjs/canvas/text.canvas.util.d.ts +20 -27
  25. package/dist/cjs/canvas/text.canvas.util.d.ts.map +1 -1
  26. package/dist/cjs/canvas/text.canvas.util.js +27 -45
  27. package/dist/cjs/canvas/text.canvas.util.js.map +1 -1
  28. package/dist/esm/canvas/canvas.helper.d.ts +9 -11
  29. package/dist/esm/canvas/canvas.helper.d.ts.map +1 -1
  30. package/dist/esm/canvas/canvas.helper.js +47 -48
  31. package/dist/esm/canvas/canvas.type.d.ts +31 -31
  32. package/dist/esm/canvas/canvas.type.d.ts.map +1 -1
  33. package/dist/esm/canvas/grid.canvas.util.d.ts +4 -4
  34. package/dist/esm/canvas/grid.canvas.util.d.ts.map +1 -1
  35. package/dist/esm/canvas/grid.canvas.util.js +21 -4
  36. package/dist/esm/canvas/image.canvas.util.d.ts +0 -1
  37. package/dist/esm/canvas/image.canvas.util.d.ts.map +1 -1
  38. package/dist/esm/canvas/image.canvas.util.js +72 -72
  39. package/dist/esm/canvas/layout.canvas.util.d.ts +16 -17
  40. package/dist/esm/canvas/layout.canvas.util.d.ts.map +1 -1
  41. package/dist/esm/canvas/layout.canvas.util.js +17 -24
  42. package/dist/esm/canvas/root.canvas.util.d.ts +4 -2
  43. package/dist/esm/canvas/root.canvas.util.d.ts.map +1 -1
  44. package/dist/esm/canvas/root.canvas.util.js +7 -3
  45. package/dist/esm/canvas/text.canvas.util.d.ts +20 -27
  46. package/dist/esm/canvas/text.canvas.util.d.ts.map +1 -1
  47. package/dist/esm/canvas/text.canvas.util.js +27 -45
  48. package/package.json +16 -9
@@ -2,6 +2,7 @@ import { Canvas } from 'skia-canvas';
2
2
  import { BoxNode } from './layout.canvas.util.js';
3
3
  import { Style } from '../constant/common.const.js';
4
4
 
5
+ // TODO: Add comprehensive unit tests for this file.
5
6
  /**
6
7
  * Node for rendering text content with rich text styling support
7
8
  * Supports color and weight variations through HTML-like tags
@@ -10,7 +11,7 @@ class TextNode extends BoxNode {
10
11
  segments = [];
11
12
  lines = [];
12
13
  static measurementContext = null;
13
- metricsString = 'Ag|\``';
14
+ metricsString = 'Ag|``';
14
15
  lineHeights = [];
15
16
  lineAscents = [];
16
17
  lineContentHeights = [];
@@ -92,8 +93,7 @@ class TextNode extends BoxNode {
92
93
  * - \b - Backspace (removed)
93
94
  * - \f - Form feed (treated as newline)
94
95
  * - \v - Vertical tab (treated as newline)
95
- *
96
- * @param input - Raw text string potentially containing escape sequences
96
+ * @param input Raw text string potentially containing escape sequences
97
97
  * @returns Processed string with escape sequences converted
98
98
  */
99
99
  processEscapeSequences(input) {
@@ -139,9 +139,8 @@ class TextNode extends BoxNode {
139
139
  * <color="red">, <color='red'>, <color=red>
140
140
  *
141
141
  * Tags can be nested and must be properly closed with </tag>
142
- *
143
- * @param input - Text string containing markup tags
144
- * @param baseStyle - Default style properties to apply to all segments
142
+ * @param input Text string containing markup tags
143
+ * @param baseStyle Default style properties to apply to all segments
145
144
  * @returns Array of styled text segments with consistent style properties
146
145
  */
147
146
  parseRichText(input, baseStyle) {
@@ -251,8 +250,7 @@ class TextNode extends BoxNode {
251
250
  * - Style: segment <i> > base fontStyle
252
251
  * - Size: segment size > base fontSize
253
252
  * - Family: base fontFamily
254
- *
255
- * @param segmentStyle - Optional TextSegment styling to override base props
253
+ * @param segmentStyle Optional TextSegment styling to override base props
256
254
  * @returns Formatted CSS font string for canvas context
257
255
  */
258
256
  getFontString(segmentStyle) {
@@ -303,12 +301,11 @@ class TextNode extends BoxNode {
303
301
  * 2. Otherwise calculating dynamic height based on largest font size per line
304
302
  * 3. Adding leading space above/below text content
305
303
  * 4. Including specified line gaps between lines
306
- *
307
- * @param widthConstraint - Maximum allowed width in pixels for text layout
308
- * @param widthMode - YogaLayout mode determining how width constraint is applied
304
+ * @param widthConstraint Maximum allowed width in pixels for text layout
305
+ * @param widthMode YogaLayout mode determining how width constraint is applied
309
306
  * @returns Calculated minimum dimensions required to render text content
310
- * - width: Total width needed for text layout
311
- * - height: Total height including line heights and gaps
307
+ * - width: Total width needed for text layout
308
+ * - height: Total height including line heights and gaps
312
309
  */
313
310
  measureText(widthConstraint, widthMode) {
314
311
  // Create measurement canvas if not exists
@@ -428,9 +425,7 @@ class TextNode extends BoxNode {
428
425
  // Calculate total content height for line
429
426
  const actualContentHeight = maxAscent + maxDescent;
430
427
  // Determine final line box height with leading
431
- const targetLineBoxHeight = typeof this.props.lineHeight === 'number' && this.props.lineHeight > 0
432
- ? this.props.lineHeight
433
- : maxFontSizeOnLine * defaultLineHeightMultiplier;
428
+ const targetLineBoxHeight = typeof this.props.lineHeight === 'number' && this.props.lineHeight > 0 ? this.props.lineHeight : maxFontSizeOnLine * defaultLineHeightMultiplier;
434
429
  // Use larger of target height or content height to prevent clipping
435
430
  const finalLineHeight = Math.max(actualContentHeight, targetLineBoxHeight);
436
431
  // Store line metrics for rendering
@@ -534,11 +529,10 @@ class TextNode extends BoxNode {
534
529
  * Wraps text segments into multiple lines while respecting width constraints and preserving styling.
535
530
  * Handles rich text attributes (color, weight, size, bold, italic) and proper word wrapping.
536
531
  * Also respects explicit newline characters (\n) for forced line breaks.
537
- *
538
- * @param ctx - Canvas rendering context used for text measurements
539
- * @param segments - Array of text segments with styling information
540
- * @param maxWidth - Maximum allowed width for each line in pixels
541
- * @param parsedWordSpacingPx - Additional spacing to add between words in pixels
532
+ * @param ctx Canvas rendering context used for text measurements
533
+ * @param segments Array of text segments with styling information
534
+ * @param maxWidth Maximum allowed width for each line in pixels
535
+ * @param parsedWordSpacingPx Additional spacing to add between words in pixels
542
536
  * @returns Array of lines, where each line contains styled text segments
543
537
  */
544
538
  wrapTextRich(ctx, segments, maxWidth, parsedWordSpacingPx) {
@@ -552,8 +546,7 @@ class TextNode extends BoxNode {
552
546
  const finalizeLine = (forceEmpty = false) => {
553
547
  // Remove trailing whitespace segments unless we're forcing an empty line
554
548
  if (!forceEmpty) {
555
- while (currentLineSegments.length > 0 &&
556
- /^\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text)) {
549
+ while (currentLineSegments.length > 0 && /^\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text)) {
557
550
  currentLineSegments.pop();
558
551
  }
559
552
  }
@@ -596,8 +589,7 @@ class TextNode extends BoxNode {
596
589
  wordWidth = ctx.measureText(wordOrSpace).width;
597
590
  wordSegment = { text: wordOrSpace, ...segmentStyle, width: wordWidth };
598
591
  }
599
- const needsSpace = currentLineSegments.length > 0 &&
600
- !/^\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text);
592
+ const needsSpace = currentLineSegments.length > 0 && !/^\s+$/.test(currentLineSegments[currentLineSegments.length - 1].text);
601
593
  const spaceToAdd = needsSpace ? spaceWidth + parsedWordSpacingPx : 0;
602
594
  if (currentLineWidth + spaceToAdd + wordWidth <= maxWidth || currentLineSegments.length === 0) {
603
595
  if (needsSpace) {
@@ -703,10 +695,9 @@ class TextNode extends BoxNode {
703
695
  /**
704
696
  * Breaks a word segment into multiple segments that each fit within the specified width constraint.
705
697
  * Maintains all styling properties (color, weight, size, bold, italic) across broken segments.
706
- *
707
- * @param ctx - Canvas rendering context used for text measurements
708
- * @param segmentToBreak - Original text segment to split
709
- * @param maxWidth - Maximum width allowed for each resulting segment
698
+ * @param ctx Canvas rendering context used for text measurements
699
+ * @param segmentToBreak Original text segment to split
700
+ * @param maxWidth Maximum width allowed for each resulting segment
710
701
  * @returns Array of TextSegments, each fitting maxWidth, or original segment if no breaking needed
711
702
  */
712
703
  breakWordRich(ctx, segmentToBreak, maxWidth) {
@@ -789,12 +780,11 @@ class TextNode extends BoxNode {
789
780
  * - Ellipsis truncation
790
781
  * - Rich text styling per segment (color, weight, size, etc)
791
782
  * - Performance optimizations (clipping, visibility checks)
792
- *
793
- * @param ctx - Canvas rendering context
794
- * @param x - Content box left position in pixels
795
- * @param y - Content box top position in pixels
796
- * @param width - Content box total width including padding
797
- * @param height - Content box total height including padding
783
+ * @param ctx Canvas rendering context
784
+ * @param x Content box left position in pixels
785
+ * @param y Content box top position in pixels
786
+ * @param width Content box total width including padding
787
+ * @param height Content box total height including padding
798
788
  */
799
789
  _renderContent(ctx, x, y, width, height) {
800
790
  super._renderContent(ctx, x, y, width, height);
@@ -995,11 +985,7 @@ class TextNode extends BoxNode {
995
985
  let applyEllipsisAfter = false;
996
986
  if (isLastRenderedLine && needsEllipsis && !isSpaceSegment) {
997
987
  const currentTotalWidth = accumulatedWidth + spaceToAddBefore + segmentWidth;
998
- const spaceNeededAfter = isLastSegmentOnLine
999
- ? 0
1000
- : isJustify
1001
- ? spacePerWordGapPlusSpacing
1002
- : spaceWidth + parsedWordSpacingPx;
988
+ const spaceNeededAfter = isLastSegmentOnLine ? 0 : isJustify ? spacePerWordGapPlusSpacing : spaceWidth + parsedWordSpacingPx;
1003
989
  if (currentTotalWidth > contentWidth - spaceNeededAfter) {
1004
990
  const availableWidthForSegment = contentWidth - accumulatedWidth - spaceToAddBefore - ellipsisWidth;
1005
991
  if (availableWidthForSegment > 0) {
@@ -1033,11 +1019,7 @@ class TextNode extends BoxNode {
1033
1019
  const remainingRenderWidth = contentX + contentWidth - currentX;
1034
1020
  if (currentSegmentRenderWidth > 0 && remainingRenderWidth > 0 && !isSpaceSegment) {
1035
1021
  ctx.textAlign = 'left';
1036
- const shadows = this.props.textShadow
1037
- ? Array.isArray(this.props.textShadow)
1038
- ? this.props.textShadow
1039
- : [this.props.textShadow]
1040
- : [];
1022
+ const shadows = this.props.textShadow ? (Array.isArray(this.props.textShadow) ? this.props.textShadow : [this.props.textShadow]) : [];
1041
1023
  ctx.save();
1042
1024
  // Draw shadows
1043
1025
  for (const shadow of shadows) {
package/package.json CHANGED
@@ -1,7 +1,18 @@
1
1
  {
2
2
  "name": "@meonode/canvas",
3
- "version": "1.0.0-beta.3",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "A declarative, component-based library for generating high-quality images on a canvas, inspired by the MeoNode UI library for React. It leverages skia-canvas for rendering and yoga-layout for flexible, CSS-like layouts.",
5
+ "keywords": [
6
+ "canvas",
7
+ "server-side",
8
+ "image-generation",
9
+ "declarative",
10
+ "component-based",
11
+ "skia-canvas",
12
+ "yoga-layout",
13
+ "node",
14
+ "typescript"
15
+ ],
5
16
  "license": "MIT",
6
17
  "author": "Ukasyah Rahmatullah Zada",
7
18
  "publishConfig": {
@@ -25,7 +36,8 @@
25
36
  "prepare": "husky"
26
37
  },
27
38
  "devDependencies": {
28
- "@rollup/plugin-alias": "^6.0.0",
39
+ "@eslint/js": "^9.39.1",
40
+ "@jest/globals": "^30.2.0",
29
41
  "@rollup/plugin-commonjs": "^29.0.0",
30
42
  "@rollup/plugin-node-resolve": "^16.0.3",
31
43
  "@rollup/plugin-typescript": "^12.3.0",
@@ -35,17 +47,12 @@
35
47
  "@semantic-release/npm": "^13.1.1",
36
48
  "@semantic-release/release-notes-generator": "^14.1.0",
37
49
  "@types/jest": "^30.0.0",
38
- "@types/lodash-es": "^4.17.12",
39
- "@types/module-alias": "^2.0.4",
40
50
  "@types/sharp": "^0.32.0",
41
- "@types/skia-canvas": "^0.9.28",
42
- "@types/tinycolor2": "^1.4.6",
43
- "@types/yoga-layout": "^3.1.0",
44
51
  "@typescript-eslint/eslint-plugin": "^8.46.3",
45
52
  "@typescript-eslint/parser": "^8.46.3",
46
53
  "eslint": "^9.39.1",
47
54
  "eslint-config-prettier": "^10.1.8",
48
- "eslint-plugin-import": "^2.32.0",
55
+ "eslint-plugin-jsdoc": "^61.1.12",
49
56
  "eslint-plugin-prettier": "^5.5.4",
50
57
  "eslint-plugin-unused-imports": "^4.3.0",
51
58
  "husky": "^9.1.7",
@@ -64,7 +71,7 @@
64
71
  "dependencies": {
65
72
  "file-type": "^21.0.0",
66
73
  "lodash-es": "^4.17.21",
67
- "module-alias": "^2.2.3",
74
+ "sharp": "^0.34.5",
68
75
  "skia-canvas": "^3.0.8",
69
76
  "tinycolor2": "^1.6.0",
70
77
  "tslib": "^2.8.1",