@emasoft/svg-matrix 1.0.14 → 1.0.16

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/bin/svg-matrix.js CHANGED
@@ -103,6 +103,7 @@ const DEFAULT_CONFIG = {
103
103
  recursive: false,
104
104
  overwrite: false,
105
105
  dryRun: false,
106
+ showCommandHelp: false, // Track if --help was requested for a specific command
106
107
  // Full flatten options - all enabled by default for TRUE flattening
107
108
  transformOnly: false, // If true, skip all resolvers (legacy behavior)
108
109
  resolveClipPaths: true, // Apply clipPath boolean intersection
@@ -499,81 +500,281 @@ function removeShapeAttrs(attrs, attrsToRemove) {
499
500
  // COMMANDS
500
501
  // ============================================================================
501
502
 
503
+ // ============================================================================
504
+ // ENHANCED CLI HELP SYSTEM
505
+ // ============================================================================
506
+
507
+ // Box drawing helpers
508
+ const supportsUnicode = () => {
509
+ if (process.platform === 'win32') {
510
+ return !!(process.env.WT_SESSION || process.env.CHCP === '65001' ||
511
+ process.env.ConEmuANSI === 'ON' || process.env.TERM_PROGRAM);
512
+ }
513
+ return true;
514
+ };
515
+
516
+ const B = supportsUnicode()
517
+ ? { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│', dot: '•', arr: '→' }
518
+ : { tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|', dot: '*', arr: '->' };
519
+
520
+ function stripAnsi(s) {
521
+ return s.replace(/\x1b\[[0-9;]*m/g, '');
522
+ }
523
+
524
+ function boxLine(content, width = 70) {
525
+ const visible = stripAnsi(content).length;
526
+ const padding = Math.max(0, width - visible - 2);
527
+ return `${colors.cyan}${B.v}${colors.reset} ${content}${' '.repeat(padding)}${colors.cyan}${B.v}${colors.reset}`;
528
+ }
529
+
530
+ function boxHeader(title, width = 70) {
531
+ const hr = B.h.repeat(width);
532
+ const visible = stripAnsi(title).length;
533
+ const padding = Math.max(0, width - visible - 2);
534
+ return `${colors.cyan}${B.v}${colors.reset} ${colors.bright}${title}${colors.reset}${' '.repeat(padding)}${colors.cyan}${B.v}${colors.reset}`;
535
+ }
536
+
537
+ function boxDivider(width = 70) {
538
+ return `${colors.cyan}${B.v}${B.h.repeat(width)}${B.v}${colors.reset}`;
539
+ }
540
+
502
541
  function showHelp() {
542
+ const W = 72;
543
+ const hr = B.h.repeat(W);
544
+
503
545
  console.log(`
504
- ${colors.cyan}${colors.bright}@emasoft/svg-matrix${colors.reset} v${VERSION}
505
- High-precision SVG matrix and transformation CLI
506
-
507
- ${colors.bright}USAGE:${colors.reset}
508
- svg-matrix <command> [options] <input> [-o <output>]
509
-
510
- ${colors.bright}COMMANDS:${colors.reset}
511
- flatten TRUE flatten: resolve ALL transform dependencies
512
- - Bakes transform attributes into coordinates
513
- - Applies clipPath boolean operations
514
- - Converts masks to clipped geometry
515
- - Expands use/symbol references inline
516
- - Instantiates markers as path geometry
517
- - Expands pattern fills to tiled geometry
518
- - Bakes gradientTransform into coordinates
519
- convert Convert shapes (rect, circle, etc.) to paths
520
- normalize Convert paths to absolute cubic Bezier curves
521
- info Show SVG file information
522
- help Show this help message
523
- version Show version number
524
-
525
- ${colors.bright}OPTIONS:${colors.reset}
526
- -o, --output <path> Output file or directory
527
- -l, --list <file> Read input files from text file
528
- -r, --recursive Process directories recursively
529
- -p, --precision <n> Decimal precision (default: 6)
530
- -f, --force Overwrite existing output files
531
- -n, --dry-run Show what would be done
532
- -q, --quiet Suppress all output except errors
533
- -v, --verbose Enable verbose/debug output
534
- --log-file <path> Write log to file
535
- -h, --help Show help
536
-
537
- ${colors.bright}FLATTEN OPTIONS:${colors.reset}
538
- --transform-only Only flatten transforms (skip resolvers)
539
- --no-clip-paths Skip clipPath boolean operations
540
- --no-masks Skip mask to clip conversion
541
- --no-use Skip use/symbol expansion
542
- --no-markers Skip marker instantiation
543
- --no-patterns Skip pattern expansion
544
- --no-gradients Skip gradient transform baking
545
-
546
- ${colors.bright}E2E VERIFICATION OPTIONS:${colors.reset}
547
- --clip-segments <n> Polygon samples for clipping (default: 64)
548
- Higher = better curve approximation, tighter tolerance
549
- Recommended: 64 (balanced), 128 (high), 256 (very high)
550
- --bezier-arcs <n> Bezier arcs for circles/ellipses (default: 8)
551
- Must be multiple of 4. Multiples of 8 are optimal (π/4).
552
- 8: ~0.0004% error (π/4 optimal base)
553
- 16: ~0.000007% error (high precision)
554
- 32: ~0.0000004% error, 64: ~0.00000001% error
555
- --e2e-tolerance <exp> E2E verification tolerance exponent (default: 1e-10)
556
- Examples: 1e-8, 1e-10, 1e-12, 1e-14
557
- Tighter tolerance requires more clip-segments
558
-
559
- ${colors.dim}Note: Mathematical verification is ALWAYS enabled.${colors.reset}
560
- ${colors.dim}Precision is non-negotiable in this library.${colors.reset}
561
-
562
- ${colors.bright}EXAMPLES:${colors.reset}
563
- svg-matrix flatten input.svg -o output.svg
564
- svg-matrix flatten ./svgs/ -o ./output/ --transform-only
565
- svg-matrix flatten --list files.txt -o ./output/ --no-patterns
566
- svg-matrix convert input.svg -o output.svg --precision 10
567
- svg-matrix info input.svg
568
-
569
- ${colors.bright}FILE LIST FORMAT:${colors.reset}
570
- One path per line. Lines starting with # are comments.
571
-
572
- ${colors.bright}DOCUMENTATION:${colors.reset}
573
- https://github.com/Emasoft/SVG-MATRIX#readme
546
+ ${colors.cyan}${B.tl}${hr}${B.tr}${colors.reset}
547
+ ${boxLine(`${colors.bright}@emasoft/svg-matrix${colors.reset} v${VERSION}`, W)}
548
+ ${boxLine(`${colors.dim}Arbitrary-precision SVG transforms with decimal.js${colors.reset}`, W)}
549
+ ${boxDivider(W)}
550
+ ${boxLine('', W)}
551
+ ${boxHeader('USAGE', W)}
552
+ ${boxLine('', W)}
553
+ ${boxLine(` svg-matrix <command> [options] <input> [-o <output>]`, W)}
554
+ ${boxLine('', W)}
555
+ ${boxDivider(W)}
556
+ ${boxLine('', W)}
557
+ ${boxHeader('COMMANDS', W)}
558
+ ${boxLine('', W)}
559
+ ${boxLine(` ${colors.green}flatten${colors.reset} TRUE flatten: resolve ALL transform dependencies`, W)}
560
+ ${boxLine(` ${colors.dim}Bakes transforms, applies clipPaths, expands use/markers${colors.reset}`, W)}
561
+ ${boxLine('', W)}
562
+ ${boxLine(` ${colors.green}convert${colors.reset} Convert shapes (rect, circle, ellipse, line) to paths`, W)}
563
+ ${boxLine(` ${colors.dim}Preserves all style attributes${colors.reset}`, W)}
564
+ ${boxLine('', W)}
565
+ ${boxLine(` ${colors.green}normalize${colors.reset} Convert all paths to absolute cubic Bezier curves`, W)}
566
+ ${boxLine(` ${colors.dim}Ideal for animation and path morphing${colors.reset}`, W)}
567
+ ${boxLine('', W)}
568
+ ${boxLine(` ${colors.green}info${colors.reset} Show SVG file information and element counts`, W)}
569
+ ${boxLine('', W)}
570
+ ${boxLine(` ${colors.green}help${colors.reset} Show this help (or: svg-matrix <command> --help)`, W)}
571
+ ${boxLine(` ${colors.green}version${colors.reset} Show version number`, W)}
572
+ ${boxLine('', W)}
573
+ ${boxDivider(W)}
574
+ ${boxLine('', W)}
575
+ ${boxHeader('GLOBAL OPTIONS', W)}
576
+ ${boxLine('', W)}
577
+ ${boxLine(` ${colors.dim}-o, --output <path>${colors.reset} Output file or directory`, W)}
578
+ ${boxLine(` ${colors.dim}-l, --list <file>${colors.reset} Read input files from text file`, W)}
579
+ ${boxLine(` ${colors.dim}-r, --recursive${colors.reset} Process directories recursively`, W)}
580
+ ${boxLine(` ${colors.dim}-p, --precision <n>${colors.reset} Decimal precision (default: 6, max: 50)`, W)}
581
+ ${boxLine(` ${colors.dim}-f, --force${colors.reset} Overwrite existing output files`, W)}
582
+ ${boxLine(` ${colors.dim}-n, --dry-run${colors.reset} Show what would be done`, W)}
583
+ ${boxLine(` ${colors.dim}-q, --quiet${colors.reset} Suppress all output except errors`, W)}
584
+ ${boxLine(` ${colors.dim}-v, --verbose${colors.reset} Enable verbose/debug output`, W)}
585
+ ${boxLine(` ${colors.dim}--log-file <path>${colors.reset} Write log to file`, W)}
586
+ ${boxLine('', W)}
587
+ ${boxDivider(W)}
588
+ ${boxLine('', W)}
589
+ ${boxHeader('FLATTEN OPTIONS', W)}
590
+ ${boxLine('', W)}
591
+ ${boxLine(` ${colors.dim}--transform-only${colors.reset} Only flatten transforms (skip resolvers)`, W)}
592
+ ${boxLine(` ${colors.dim}--no-clip-paths${colors.reset} Skip clipPath boolean operations`, W)}
593
+ ${boxLine(` ${colors.dim}--no-masks${colors.reset} Skip mask to clip conversion`, W)}
594
+ ${boxLine(` ${colors.dim}--no-use${colors.reset} Skip use/symbol expansion`, W)}
595
+ ${boxLine(` ${colors.dim}--no-markers${colors.reset} Skip marker instantiation`, W)}
596
+ ${boxLine(` ${colors.dim}--no-patterns${colors.reset} Skip pattern expansion`, W)}
597
+ ${boxLine(` ${colors.dim}--no-gradients${colors.reset} Skip gradient transform baking`, W)}
598
+ ${boxLine('', W)}
599
+ ${boxDivider(W)}
600
+ ${boxLine('', W)}
601
+ ${boxHeader('PRECISION OPTIONS', W)}
602
+ ${boxLine('', W)}
603
+ ${boxLine(` ${colors.dim}--clip-segments <n>${colors.reset} Polygon samples for clipping (default: 64)`, W)}
604
+ ${boxLine(` ${colors.dim}64=balanced, 128=high, 256=very high${colors.reset}`, W)}
605
+ ${boxLine('', W)}
606
+ ${boxLine(` ${colors.dim}--bezier-arcs <n>${colors.reset} Bezier arcs for curves (default: 8)`, W)}
607
+ ${boxLine(` ${colors.dim}Must be multiple of 4. Error rates:${colors.reset}`, W)}
608
+ ${boxLine(` ${colors.dim}8: 0.0004%, 16: 0.000007%, 64: 0.00000001%${colors.reset}`, W)}
609
+ ${boxLine('', W)}
610
+ ${boxLine(` ${colors.dim}--e2e-tolerance <exp>${colors.reset} Verification tolerance (default: 1e-10)`, W)}
611
+ ${boxLine(` ${colors.dim}Examples: 1e-8, 1e-10, 1e-12, 1e-14${colors.reset}`, W)}
612
+ ${boxLine('', W)}
613
+ ${boxLine(` ${colors.yellow}${B.dot} Mathematical verification is ALWAYS enabled${colors.reset}`, W)}
614
+ ${boxLine(` ${colors.yellow}${B.dot} Precision is non-negotiable in this library${colors.reset}`, W)}
615
+ ${boxLine('', W)}
616
+ ${boxDivider(W)}
617
+ ${boxLine('', W)}
618
+ ${boxHeader('EXAMPLES', W)}
619
+ ${boxLine('', W)}
620
+ ${boxLine(` ${colors.green}svg-matrix flatten${colors.reset} input.svg -o output.svg`, W)}
621
+ ${boxLine(` ${colors.green}svg-matrix flatten${colors.reset} ./svgs/ -o ./out/ --transform-only`, W)}
622
+ ${boxLine(` ${colors.green}svg-matrix flatten${colors.reset} --list files.txt -o ./out/ --no-patterns`, W)}
623
+ ${boxLine(` ${colors.green}svg-matrix convert${colors.reset} input.svg -o output.svg -p 10`, W)}
624
+ ${boxLine(` ${colors.green}svg-matrix info${colors.reset} input.svg`, W)}
625
+ ${boxLine('', W)}
626
+ ${boxDivider(W)}
627
+ ${boxLine('', W)}
628
+ ${boxHeader('JAVASCRIPT API', W)}
629
+ ${boxLine('', W)}
630
+ ${boxLine(` import { Matrix, Vector, Transforms2D } from '@emasoft/svg-matrix'`, W)}
631
+ ${boxLine('', W)}
632
+ ${boxLine(` ${colors.green}${B.dot} Matrix${colors.reset} Arbitrary-precision matrix operations`, W)}
633
+ ${boxLine(` ${colors.green}${B.dot} Vector${colors.reset} High-precision vector math`, W)}
634
+ ${boxLine(` ${colors.green}${B.dot} Transforms2D${colors.reset} rotate, scale, translate, skew, reflect`, W)}
635
+ ${boxLine(` ${colors.green}${B.dot} Transforms3D${colors.reset} 3D affine transformations`, W)}
636
+ ${boxLine('', W)}
637
+ ${colors.cyan}${B.bl}${hr}${B.br}${colors.reset}
638
+
639
+ ${colors.cyan}Docs:${colors.reset} https://github.com/Emasoft/SVG-MATRIX#readme
574
640
  `);
575
641
  }
576
642
 
643
+ function showCommandHelp(command) {
644
+ const W = 72;
645
+ const hr = B.h.repeat(W);
646
+
647
+ const commandHelp = {
648
+ flatten: `
649
+ ${colors.cyan}${B.tl}${hr}${B.tr}${colors.reset}
650
+ ${boxLine(`${colors.bright}svg-matrix flatten${colors.reset} - TRUE SVG Flattening`, W)}
651
+ ${boxDivider(W)}
652
+ ${boxLine('', W)}
653
+ ${boxLine(`Resolves ALL transform dependencies to produce a "flat" SVG where`, W)}
654
+ ${boxLine(`every coordinate is in the root coordinate system.`, W)}
655
+ ${boxLine('', W)}
656
+ ${boxHeader('WHAT IT DOES', W)}
657
+ ${boxLine('', W)}
658
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Bakes transform attributes into path coordinates`, W)}
659
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Applies clipPath as boolean intersection operations`, W)}
660
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Converts masks to equivalent clipped geometry`, W)}
661
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Expands <use> and <symbol> references inline`, W)}
662
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Instantiates markers as actual path geometry`, W)}
663
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Expands pattern fills to tiled geometry`, W)}
664
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Bakes gradientTransform into gradient coordinates`, W)}
665
+ ${boxLine('', W)}
666
+ ${boxHeader('USAGE', W)}
667
+ ${boxLine('', W)}
668
+ ${boxLine(` svg-matrix flatten <input> [options]`, W)}
669
+ ${boxLine('', W)}
670
+ ${boxHeader('EXAMPLES', W)}
671
+ ${boxLine('', W)}
672
+ ${boxLine(` ${colors.dim}# Full flatten (all resolvers enabled)${colors.reset}`, W)}
673
+ ${boxLine(` svg-matrix flatten input.svg -o output.svg`, W)}
674
+ ${boxLine('', W)}
675
+ ${boxLine(` ${colors.dim}# Transform-only mode (legacy, faster)${colors.reset}`, W)}
676
+ ${boxLine(` svg-matrix flatten input.svg -o out.svg --transform-only`, W)}
677
+ ${boxLine('', W)}
678
+ ${boxLine(` ${colors.dim}# High-precision mode for complex curves${colors.reset}`, W)}
679
+ ${boxLine(` svg-matrix flatten in.svg -o out.svg --clip-segments 128`, W)}
680
+ ${boxLine('', W)}
681
+ ${boxLine(` ${colors.dim}# Skip specific resolvers${colors.reset}`, W)}
682
+ ${boxLine(` svg-matrix flatten in.svg -o out.svg --no-patterns --no-markers`, W)}
683
+ ${boxLine('', W)}
684
+ ${colors.cyan}${B.bl}${hr}${B.br}${colors.reset}
685
+ `,
686
+ convert: `
687
+ ${colors.cyan}${B.tl}${hr}${B.tr}${colors.reset}
688
+ ${boxLine(`${colors.bright}svg-matrix convert${colors.reset} - Shape to Path Conversion`, W)}
689
+ ${boxDivider(W)}
690
+ ${boxLine('', W)}
691
+ ${boxLine(`Converts all basic shapes to <path> elements while preserving`, W)}
692
+ ${boxLine(`all style and presentation attributes.`, W)}
693
+ ${boxLine('', W)}
694
+ ${boxHeader('SUPPORTED SHAPES', W)}
695
+ ${boxLine('', W)}
696
+ ${boxLine(` ${colors.green}${B.dot} rect${colors.reset} ${B.arr} path (with optional rounded corners)`, W)}
697
+ ${boxLine(` ${colors.green}${B.dot} circle${colors.reset} ${B.arr} path (4 cubic Bezier arcs)`, W)}
698
+ ${boxLine(` ${colors.green}${B.dot} ellipse${colors.reset} ${B.arr} path (4 cubic Bezier arcs)`, W)}
699
+ ${boxLine(` ${colors.green}${B.dot} line${colors.reset} ${B.arr} path (M...L command)`, W)}
700
+ ${boxLine(` ${colors.green}${B.dot} polygon${colors.reset} ${B.arr} path (closed polyline)`, W)}
701
+ ${boxLine(` ${colors.green}${B.dot} polyline${colors.reset} ${B.arr} path (open polyline)`, W)}
702
+ ${boxLine('', W)}
703
+ ${boxHeader('USAGE', W)}
704
+ ${boxLine('', W)}
705
+ ${boxLine(` svg-matrix convert <input> -o <output> [-p <precision>]`, W)}
706
+ ${boxLine('', W)}
707
+ ${boxHeader('EXAMPLE', W)}
708
+ ${boxLine('', W)}
709
+ ${boxLine(` svg-matrix convert shapes.svg -o paths.svg --precision 8`, W)}
710
+ ${boxLine('', W)}
711
+ ${colors.cyan}${B.bl}${hr}${B.br}${colors.reset}
712
+ `,
713
+ normalize: `
714
+ ${colors.cyan}${B.tl}${hr}${B.tr}${colors.reset}
715
+ ${boxLine(`${colors.bright}svg-matrix normalize${colors.reset} - Path Normalization`, W)}
716
+ ${boxDivider(W)}
717
+ ${boxLine('', W)}
718
+ ${boxLine(`Converts all path commands to absolute cubic Bezier curves.`, W)}
719
+ ${boxLine(`Ideal for path morphing, animation, and consistent processing.`, W)}
720
+ ${boxLine('', W)}
721
+ ${boxHeader('WHAT IT DOES', W)}
722
+ ${boxLine('', W)}
723
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Converts relative commands (m,l,c,s,q,t,a) to absolute`, W)}
724
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Converts all curves to cubic Beziers (C commands)`, W)}
725
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Expands shorthand (S,T) to full curves`, W)}
726
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Converts arcs (A) to cubic Bezier approximations`, W)}
727
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Converts lines (L,H,V) to degenerate cubics`, W)}
728
+ ${boxLine('', W)}
729
+ ${boxHeader('OUTPUT FORMAT', W)}
730
+ ${boxLine('', W)}
731
+ ${boxLine(` All paths become: M x y C x1 y1 x2 y2 x y C ... Z`, W)}
732
+ ${boxLine('', W)}
733
+ ${boxHeader('USAGE', W)}
734
+ ${boxLine('', W)}
735
+ ${boxLine(` svg-matrix normalize <input> -o <output>`, W)}
736
+ ${boxLine('', W)}
737
+ ${boxHeader('EXAMPLE', W)}
738
+ ${boxLine('', W)}
739
+ ${boxLine(` svg-matrix normalize complex.svg -o normalized.svg`, W)}
740
+ ${boxLine('', W)}
741
+ ${colors.cyan}${B.bl}${hr}${B.br}${colors.reset}
742
+ `,
743
+ info: `
744
+ ${colors.cyan}${B.tl}${hr}${B.tr}${colors.reset}
745
+ ${boxLine(`${colors.bright}svg-matrix info${colors.reset} - SVG File Information`, W)}
746
+ ${boxDivider(W)}
747
+ ${boxLine('', W)}
748
+ ${boxLine(`Displays detailed information about an SVG file including`, W)}
749
+ ${boxLine(`dimensions, element counts, and structure analysis.`, W)}
750
+ ${boxLine('', W)}
751
+ ${boxHeader('INFORMATION SHOWN', W)}
752
+ ${boxLine('', W)}
753
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} File path and size`, W)}
754
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Dimensions (viewBox, width, height)`, W)}
755
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Element counts (paths, shapes, groups)`, W)}
756
+ ${boxLine(` ${colors.green}${B.dot}${colors.reset} Transform attribute count`, W)}
757
+ ${boxLine('', W)}
758
+ ${boxHeader('USAGE', W)}
759
+ ${boxLine('', W)}
760
+ ${boxLine(` svg-matrix info <input>`, W)}
761
+ ${boxLine(` svg-matrix info <folder> -r ${colors.dim}# recursive${colors.reset}`, W)}
762
+ ${boxLine('', W)}
763
+ ${boxHeader('EXAMPLE', W)}
764
+ ${boxLine('', W)}
765
+ ${boxLine(` svg-matrix info logo.svg`, W)}
766
+ ${boxLine('', W)}
767
+ ${colors.cyan}${B.bl}${hr}${B.br}${colors.reset}
768
+ `
769
+ };
770
+
771
+ if (commandHelp[command]) {
772
+ console.log(commandHelp[command]);
773
+ } else {
774
+ showHelp();
775
+ }
776
+ }
777
+
577
778
  function showVersion() { console.log(`@emasoft/svg-matrix v${VERSION}`); }
578
779
 
579
780
  /**
@@ -1002,7 +1203,14 @@ function parseArgs(args) {
1002
1203
  case '-q': case '--quiet': cfg.quiet = true; break;
1003
1204
  case '-v': case '--verbose': cfg.verbose = true; break;
1004
1205
  case '--log-file': cfg.logFile = args[++i]; break;
1005
- case '-h': case '--help': cfg.command = 'help'; break;
1206
+ case '-h': case '--help':
1207
+ // If a command is already set (not 'help'), show command-specific help
1208
+ if (cfg.command !== 'help' && ['flatten', 'convert', 'normalize', 'info'].includes(cfg.command)) {
1209
+ cfg.showCommandHelp = true;
1210
+ } else {
1211
+ cfg.command = 'help';
1212
+ }
1213
+ break;
1006
1214
  case '--version': cfg.command = 'version'; break;
1007
1215
  // Full flatten pipeline options
1008
1216
  case '--transform-only': cfg.transformOnly = true; break;
@@ -1096,6 +1304,12 @@ async function main() {
1096
1304
 
1097
1305
  config = parseArgs(args);
1098
1306
 
1307
+ // Handle command-specific help (e.g., `svg-matrix flatten --help`)
1308
+ if (config.showCommandHelp) {
1309
+ showCommandHelp(config.command);
1310
+ process.exit(CONSTANTS.EXIT_SUCCESS);
1311
+ }
1312
+
1099
1313
  if (config.logFile) {
1100
1314
  try {
1101
1315
  if (existsSync(config.logFile)) unlinkSync(config.logFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emasoft/svg-matrix",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Arbitrary-precision matrix, vector and affine transformation library for JavaScript using decimal.js",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -3,11 +3,14 @@
3
3
  * @fileoverview Post-install welcome message for @emasoft/svg-matrix
4
4
  * Displays a summary of CLI commands and API functions after npm install.
5
5
  *
6
+ * Note: npm 7+ suppresses lifecycle script output by default.
7
+ * We write directly to /dev/tty on Unix to bypass this.
8
+ *
6
9
  * @module scripts/postinstall
7
10
  * @license MIT
8
11
  */
9
12
 
10
- import { readFileSync } from 'fs';
13
+ import { readFileSync, createWriteStream, existsSync } from 'fs';
11
14
  import { fileURLToPath } from 'url';
12
15
  import { dirname, join } from 'path';
13
16
 
@@ -68,9 +71,24 @@ function pad(s, w) {
68
71
  return s + ' '.repeat(Math.max(0, w - visible));
69
72
  }
70
73
 
74
+ // Get a writable stream that bypasses npm's output suppression
75
+ function getOutputStream() {
76
+ // On Unix, try to write directly to /dev/tty (the terminal)
77
+ // This bypasses npm's stdout/stderr redirection
78
+ if (process.platform !== 'win32' && existsSync('/dev/tty')) {
79
+ try {
80
+ return createWriteStream('/dev/tty');
81
+ } catch {
82
+ // Fall back to stderr
83
+ }
84
+ }
85
+ // On Windows or if /dev/tty fails, use stderr
86
+ return process.stderr;
87
+ }
88
+
71
89
  function showWelcome() {
90
+ // Skip in CI environments - no need to clutter build logs
72
91
  if (isCI()) process.exit(0);
73
- if (!process.stdout.isTTY) process.exit(0);
74
92
 
75
93
  const version = getVersion();
76
94
  const c = getColors(shouldDisableColors());
@@ -84,7 +102,11 @@ function showWelcome() {
84
102
  const hr = B.h.repeat(W);
85
103
  const R = (s) => pad(s, W);
86
104
 
87
- console.log(`
105
+ // Get output stream that bypasses npm suppression
106
+ const out = getOutputStream();
107
+ const write = (s) => out.write(s);
108
+
109
+ write(`
88
110
  ${c.cyan}${B.tl}${hr}${B.tr}${c.reset}
89
111
  ${c.cyan}${B.v}${c.reset}${R(` ${c.bright}@emasoft/svg-matrix${c.reset} v${version}`)}${c.cyan}${B.v}${c.reset}
90
112
  ${c.cyan}${B.v}${c.reset}${R(` ${c.dim}Arbitrary-precision SVG transforms with decimal.js${c.reset}`)}${c.cyan}${B.v}${c.reset}
@@ -115,10 +137,10 @@ ${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot} Transforms3D${c.reset} 3D af
115
137
  ${c.cyan}${B.v}${c.reset}${R('')}${c.cyan}${B.v}${c.reset}
116
138
  ${c.cyan}${B.v}${hr}${B.v}${c.reset}
117
139
  ${c.cyan}${B.v}${c.reset}${R('')}${c.cyan}${B.v}${c.reset}
118
- ${c.cyan}${B.v}${c.reset}${R(` ${c.yellow}New in v1.0.13:${c.reset}`)}${c.cyan}${B.v}${c.reset}
140
+ ${c.cyan}${B.v}${c.reset}${R(` ${c.yellow}New in v1.0.16:${c.reset}`)}${c.cyan}${B.v}${c.reset}
141
+ ${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} Enhanced CLI help: svg-matrix <command> --help`)}${c.cyan}${B.v}${c.reset}
119
142
  ${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} High-precision Bezier circle/ellipse approximation`)}${c.cyan}${B.v}${c.reset}
120
- ${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} E2E verification for clip-path operations`)}${c.cyan}${B.v}${c.reset}
121
- ${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} Configurable: --clip-segments, --bezier-arcs, --e2e-tolerance`)}${c.cyan}${B.v}${c.reset}
143
+ ${c.cyan}${B.v}${c.reset}${R(` ${c.green}${B.dot}${c.reset} E2E verification always enabled for precision`)}${c.cyan}${B.v}${c.reset}
122
144
  ${c.cyan}${B.v}${c.reset}${R('')}${c.cyan}${B.v}${c.reset}
123
145
  ${c.cyan}${B.bl}${hr}${B.br}${c.reset}
124
146
 
package/src/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * SVG path conversion, and 2D/3D affine transformations using Decimal.js.
6
6
  *
7
7
  * @module @emasoft/svg-matrix
8
- * @version 1.0.12
8
+ * @version 1.0.16
9
9
  * @license MIT
10
10
  *
11
11
  * @example
@@ -58,7 +58,7 @@ Decimal.set({ precision: 80 });
58
58
  * Library version
59
59
  * @constant {string}
60
60
  */
61
- export const VERSION = '1.0.12';
61
+ export const VERSION = '1.0.16';
62
62
 
63
63
  /**
64
64
  * Default precision for path output (decimal places)