@macrulez/mediatoolz 0.0.0-stage → 0.1.1

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 (174) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -2
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +243 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/image-batch/backup.d.ts +22 -0
  7. package/dist/commands/image-batch/backup.js +55 -0
  8. package/dist/commands/image-batch/backup.js.map +1 -0
  9. package/dist/commands/image-batch/cache.d.ts +15 -0
  10. package/dist/commands/image-batch/cache.js +38 -0
  11. package/dist/commands/image-batch/cache.js.map +1 -0
  12. package/dist/commands/image-batch/cli.d.ts +15 -0
  13. package/dist/commands/image-batch/cli.js +323 -0
  14. package/dist/commands/image-batch/cli.js.map +1 -0
  15. package/dist/commands/image-batch/codecs.d.ts +38 -0
  16. package/dist/commands/image-batch/codecs.js +366 -0
  17. package/dist/commands/image-batch/codecs.js.map +1 -0
  18. package/dist/commands/image-batch/config.d.ts +121 -0
  19. package/dist/commands/image-batch/config.js +690 -0
  20. package/dist/commands/image-batch/config.js.map +1 -0
  21. package/dist/commands/image-batch/errors.d.ts +5 -0
  22. package/dist/commands/image-batch/errors.js +8 -0
  23. package/dist/commands/image-batch/errors.js.map +1 -0
  24. package/dist/commands/image-batch/geometry.d.ts +26 -0
  25. package/dist/commands/image-batch/geometry.js +90 -0
  26. package/dist/commands/image-batch/geometry.js.map +1 -0
  27. package/dist/commands/image-batch/inputs.d.ts +22 -0
  28. package/dist/commands/image-batch/inputs.js +87 -0
  29. package/dist/commands/image-batch/inputs.js.map +1 -0
  30. package/dist/commands/image-batch/manage-cli.d.ts +2 -0
  31. package/dist/commands/image-batch/manage-cli.js +276 -0
  32. package/dist/commands/image-batch/manage-cli.js.map +1 -0
  33. package/dist/commands/image-batch/manage.d.ts +51 -0
  34. package/dist/commands/image-batch/manage.js +1041 -0
  35. package/dist/commands/image-batch/manage.js.map +1 -0
  36. package/dist/commands/image-batch/overwrite.d.ts +14 -0
  37. package/dist/commands/image-batch/overwrite.js +31 -0
  38. package/dist/commands/image-batch/overwrite.js.map +1 -0
  39. package/dist/commands/image-batch/plan.d.ts +86 -0
  40. package/dist/commands/image-batch/plan.js +396 -0
  41. package/dist/commands/image-batch/plan.js.map +1 -0
  42. package/dist/commands/image-batch/process.d.ts +8 -0
  43. package/dist/commands/image-batch/process.js +97 -0
  44. package/dist/commands/image-batch/process.js.map +1 -0
  45. package/dist/commands/image-batch/report.d.ts +6 -0
  46. package/dist/commands/image-batch/report.js +251 -0
  47. package/dist/commands/image-batch/report.js.map +1 -0
  48. package/dist/commands/image-batch/restore.d.ts +29 -0
  49. package/dist/commands/image-batch/restore.js +125 -0
  50. package/dist/commands/image-batch/restore.js.map +1 -0
  51. package/dist/commands/image-batch/run.d.ts +127 -0
  52. package/dist/commands/image-batch/run.js +656 -0
  53. package/dist/commands/image-batch/run.js.map +1 -0
  54. package/dist/commands/image-batch/sharpen-ask.d.ts +6 -0
  55. package/dist/commands/image-batch/sharpen-ask.js +53 -0
  56. package/dist/commands/image-batch/sharpen-ask.js.map +1 -0
  57. package/dist/commands/image-batch/sharpen-manage.d.ts +26 -0
  58. package/dist/commands/image-batch/sharpen-manage.js +238 -0
  59. package/dist/commands/image-batch/sharpen-manage.js.map +1 -0
  60. package/dist/commands/image-batch/sharpen-presets.d.ts +24 -0
  61. package/dist/commands/image-batch/sharpen-presets.js +145 -0
  62. package/dist/commands/image-batch/sharpen-presets.js.map +1 -0
  63. package/dist/commands/image-batch/sharpen.d.ts +46 -0
  64. package/dist/commands/image-batch/sharpen.js +159 -0
  65. package/dist/commands/image-batch/sharpen.js.map +1 -0
  66. package/dist/commands/image-batch/template.d.ts +45 -0
  67. package/dist/commands/image-batch/template.js +142 -0
  68. package/dist/commands/image-batch/template.js.map +1 -0
  69. package/dist/commands/image-hash/cache.d.ts +14 -0
  70. package/dist/commands/image-hash/cache.js +40 -0
  71. package/dist/commands/image-hash/cache.js.map +1 -0
  72. package/dist/commands/image-hash/core.d.ts +61 -0
  73. package/dist/commands/image-hash/core.js +302 -0
  74. package/dist/commands/image-hash/core.js.map +1 -0
  75. package/dist/commands/image-hash/hash.d.ts +21 -0
  76. package/dist/commands/image-hash/hash.js +104 -0
  77. package/dist/commands/image-hash/hash.js.map +1 -0
  78. package/dist/commands/image-hash/report.d.ts +3 -0
  79. package/dist/commands/image-hash/report.js +96 -0
  80. package/dist/commands/image-hash/report.js.map +1 -0
  81. package/dist/commands/image-hash/run.d.ts +61 -0
  82. package/dist/commands/image-hash/run.js +317 -0
  83. package/dist/commands/image-hash/run.js.map +1 -0
  84. package/dist/commands/image-hash/sharp-loader.d.ts +64 -0
  85. package/dist/commands/image-hash/sharp-loader.js +81 -0
  86. package/dist/commands/image-hash/sharp-loader.js.map +1 -0
  87. package/dist/commands/ui/cli.d.ts +2 -0
  88. package/dist/commands/ui/cli.js +78 -0
  89. package/dist/commands/ui/cli.js.map +1 -0
  90. package/dist/commands/ui/fs-routes.d.ts +7 -0
  91. package/dist/commands/ui/fs-routes.js +135 -0
  92. package/dist/commands/ui/fs-routes.js.map +1 -0
  93. package/dist/commands/ui/http.d.ts +41 -0
  94. package/dist/commands/ui/http.js +81 -0
  95. package/dist/commands/ui/http.js.map +1 -0
  96. package/dist/commands/ui/jobs.d.ts +15 -0
  97. package/dist/commands/ui/jobs.js +73 -0
  98. package/dist/commands/ui/jobs.js.map +1 -0
  99. package/dist/commands/ui/modules/image-batch-backups.d.ts +2 -0
  100. package/dist/commands/ui/modules/image-batch-backups.js +116 -0
  101. package/dist/commands/ui/modules/image-batch-backups.js.map +1 -0
  102. package/dist/commands/ui/modules/image-batch-manage.d.ts +2 -0
  103. package/dist/commands/ui/modules/image-batch-manage.js +245 -0
  104. package/dist/commands/ui/modules/image-batch-manage.js.map +1 -0
  105. package/dist/commands/ui/modules/image-batch.d.ts +2 -0
  106. package/dist/commands/ui/modules/image-batch.js +225 -0
  107. package/dist/commands/ui/modules/image-batch.js.map +1 -0
  108. package/dist/commands/ui/modules/image-hash.d.ts +2 -0
  109. package/dist/commands/ui/modules/image-hash.js +158 -0
  110. package/dist/commands/ui/modules/image-hash.js.map +1 -0
  111. package/dist/commands/ui/modules/index.d.ts +2 -0
  112. package/dist/commands/ui/modules/index.js +4 -0
  113. package/dist/commands/ui/modules/index.js.map +1 -0
  114. package/dist/commands/ui/open-browser.d.ts +1 -0
  115. package/dist/commands/ui/open-browser.js +18 -0
  116. package/dist/commands/ui/open-browser.js.map +1 -0
  117. package/dist/commands/ui/scan.d.ts +20 -0
  118. package/dist/commands/ui/scan.js +57 -0
  119. package/dist/commands/ui/scan.js.map +1 -0
  120. package/dist/commands/ui/server.d.ts +18 -0
  121. package/dist/commands/ui/server.js +196 -0
  122. package/dist/commands/ui/server.js.map +1 -0
  123. package/dist/format/bytes.d.ts +2 -0
  124. package/dist/format/bytes.js +17 -0
  125. package/dist/format/bytes.js.map +1 -0
  126. package/dist/format/colors.d.ts +12 -0
  127. package/dist/format/colors.js +15 -0
  128. package/dist/format/colors.js.map +1 -0
  129. package/dist/format/diff-render.d.ts +2 -0
  130. package/dist/format/diff-render.js +35 -0
  131. package/dist/format/diff-render.js.map +1 -0
  132. package/dist/format/file-list.d.ts +6 -0
  133. package/dist/format/file-list.js +15 -0
  134. package/dist/format/file-list.js.map +1 -0
  135. package/dist/format/findings-list.d.ts +7 -0
  136. package/dist/format/findings-list.js +14 -0
  137. package/dist/format/findings-list.js.map +1 -0
  138. package/dist/format/style.d.ts +20 -0
  139. package/dist/format/style.js +36 -0
  140. package/dist/format/style.js.map +1 -0
  141. package/dist/format/table.d.ts +8 -0
  142. package/dist/format/table.js +44 -0
  143. package/dist/format/table.js.map +1 -0
  144. package/dist/format/vibes.d.ts +13 -0
  145. package/dist/format/vibes.js +82 -0
  146. package/dist/format/vibes.js.map +1 -0
  147. package/dist/index.d.ts +9 -0
  148. package/dist/index.js +6 -0
  149. package/dist/index.js.map +1 -0
  150. package/dist/ui/assets/ImageBatchPage-CY5Zbg4t.css +1 -0
  151. package/dist/ui/assets/ImageBatchPage-DVaFnQY8.js +4 -0
  152. package/dist/ui/assets/ImageHashPage-DpyJdByU.js +1 -0
  153. package/dist/ui/assets/ImageHashPage-chcY_Dzm.css +1 -0
  154. package/dist/ui/assets/SourcePicker-D8aZjDwP.css +1 -0
  155. package/dist/ui/assets/SourcePicker-eKj_VZzO.js +1 -0
  156. package/dist/ui/assets/index-D1s-23_t.css +1 -0
  157. package/dist/ui/assets/index-Dzvs2f2Q.js +3 -0
  158. package/dist/ui/index.html +17 -0
  159. package/dist/utils/closest.d.ts +2 -0
  160. package/dist/utils/closest.js +35 -0
  161. package/dist/utils/closest.js.map +1 -0
  162. package/dist/utils/glob.d.ts +4 -0
  163. package/dist/utils/glob.js +86 -0
  164. package/dist/utils/glob.js.map +1 -0
  165. package/dist/utils/split-list.d.ts +1 -0
  166. package/dist/utils/split-list.js +4 -0
  167. package/dist/utils/split-list.js.map +1 -0
  168. package/dist/utils/tty.d.ts +60 -0
  169. package/dist/utils/tty.js +390 -0
  170. package/dist/utils/tty.js.map +1 -0
  171. package/dist/utils/walk.d.ts +21 -0
  172. package/dist/utils/walk.js +116 -0
  173. package/dist/utils/walk.js.map +1 -0
  174. package/package.json +82 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Danil Lisin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,183 @@
1
- # Temporary Holding Version
1
+ # **MediaToolz**
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ![MediaToolz](https://github.com/macrulezru/assets/blob/master/packages-images/mediatoolz.webp?raw=true)
4
+
5
+ A small toolbox of CLI commands for images: resize, convert and recompress
6
+ them in bulk by rules, and generate compact placeholders for them. One
7
+ binary, one subcommand per job, each safe by default (a preview first, a
8
+ backup before anything is overwritten).
9
+
10
+ It started as part of [devtoolz](https://www.npmjs.com/package/@macrulez/devtoolz)
11
+ and lives in its own package since devtoolz 0.5.0.
12
+
13
+ ## Features
14
+
15
+ - **`image-hash`** — generates [hazehash](https://github.com/macrulezru/hazehash),
16
+ [blurhash](https://blurha.sh) and/or
17
+ [thumbhash](https://evanw.github.io/thumbhash/) placeholders for raster
18
+ images (jpg, png, webp, gif, avif, tiff). Takes files and/or
19
+ directories — several of each, comma-separated too — and `-r` walks
20
+ subdirectories. Prints to stdout by default; `-o <file>` collects
21
+ everything into one file, `--per-file` / `--out-dir <dir>` writes a
22
+ file per image (`photo.jpg` → `photo.jpg.blurhash.txt`, with
23
+ `--suffix` and `--out-ext` to rename). Formats: `json`, `plain` (just
24
+ the hash text, no newline), `csv`, and `ts`/`js` modules you can import
25
+ straight into the app. Without `-t` it makes blurhash and thumbhash; `-t hazehash` adds the
26
+ more compact and more accurate hazehash (`--budget` sets its size in
27
+ bytes, 28 by default). Besides the hashes it can produce the
28
+ dominant color and a tiny PNG preview (`-t all`), accepts `http(s)`
29
+ URLs and path lists from a file or stdin, and can keep an output file in
30
+ sync: `--cache` skips unchanged images, `--update --prune` merges into
31
+ the existing file, `--check` fails with exit code 1 when it is stale
32
+ (made for CI). `--dry-run` writes nothing and shows the result as a
33
+ table instead. Uses the native `sharp` library, which comes
34
+ with the package — see Requirements.
35
+ - **`image-batch`** — produces resized, converted and recompressed versions
36
+ of raster images in bulk, by rules kept in a reusable **config** or given
37
+ as flags: `widths`, `heights`, `size`, `longEdge` / `shortEdge` (the same for
38
+ landscape and portrait), `megapixels`, `percent`, `scale` (`@2x`),
39
+ `matchOrientation`, `maxBytes` (`--max-size 200KB` lowers the quality as far
40
+ as needed), `formats` (jpg,
41
+ png, webp, avif, gif, tiff, heif or `original`), `fit` (`inside`,
42
+ `cover`, `contain`, …), codec options (`quality` as a number or a level
43
+ `low`/`medium`/`high`/`best`, `mozjpeg`, a png `palette`, webp `lossless`,
44
+ avif `effort`, …) and file names from a template
45
+ (`{dir}/{name}-{width}w.{format}`, with `{ext}`, `{index:3}`, `{hash}`,
46
+ `{scale}`, `{date}`). The config holds only the rules; the folders are
47
+ chosen on every run. Where the results go is one of three: `-o <dir>` — a
48
+ separate folder that repeats the source structure (`--flat` drops it);
49
+ `--beside` — next to each source; `--replace` — over the sources
50
+ themselves, each in its own format, to shrink oversized originals or
51
+ recompress them in place. `--replace` is guarded: it asks for
52
+ confirmation (`--yes` without a terminal), copies the originals to
53
+ `.image-batch-backup/<date>/` first with a `journal.json`, replaces a file
54
+ only if the result is smaller, never processes the same file twice with
55
+ the same settings, and `--dry-run` shows the real before/after sizes;
56
+ `image-batch restore <backup>` puts the originals back. Without a size
57
+ flag it only converts the format. `-r` walks
58
+ subfolders, `-i/--select` ticks files from an interactive list, `--list`
59
+ only prints them. A result an earlier run already made is skipped; any
60
+ other existing file is a conflict — a terminal asks once (overwrite this
61
+ / this and all next / skip / skip all / quit), without a terminal it is
62
+ skipped with exit code 1, or use `--overwrite`, `--skip-existing`,
63
+ `--no-overwrite`. `--hazehash` (`--budget`), `--blurhash`, `--thumbhash`,
64
+ `--dominant-color` with `--emit <file>` write a manifest of everything
65
+ produced, with the placeholders. `init` creates configs, and `config` shows, edits,
66
+ copies and deletes them (`.mediatoolz/image-batch/` in the project or
67
+ `~/.mediatoolz/image-batch/` for all projects). `sharpen` adds output
68
+ sharpening after the resize — by target and amount, with `radius`,
69
+ `flat`, `jagged` and `threshold` for fine control — and `image-batch
70
+ sharpen` saves such settings as presets that configs and `--sharpen <name>`
71
+ use by name. Uses `sharp` too — see
72
+ Requirements.
73
+ - **`ui`** — starts a local web interface and opens it in the browser, so the
74
+ commands can be run with forms, tables and previews instead of flags. Two
75
+ modules: **Image Hash** (a gallery of the placeholders decoded next to each
76
+ image) and **Image Batch** (convert, edit configs and sharpening presets with
77
+ a before and after preview, restore backups). It is part of the package,
78
+ listens on `127.0.0.1` only behind a one-time token, and writes nothing
79
+ without an explicit choice. `--port`, `--no-open`, `--cwd`.
80
+
81
+ `image-hash` writes files only when you name an output (`-o`, `--per-file`,
82
+ `--out-dir`) and prints to stdout otherwise; `image-batch` writes into the
83
+ folder given with `-o`, next to the sources with `--beside`, and over them
84
+ only with `--replace` — after a confirmation, with a backup. Every command
85
+ supports `--json` for machine-readable output.
86
+
87
+ ## Requirements
88
+
89
+ - Node.js 20+
90
+ - The [`sharp`](https://sharp.pixelplumbing.com) image library comes with
91
+ the package: npm picks its prebuilt binary for your platform during the
92
+ install, so there is nothing to set up. Only if that binary is missing
93
+ (an install with `--omit=optional`, an unsupported platform) does the
94
+ first run offer to install `sharp` into `~/.mediatoolz/deps` (`-y` /
95
+ `--yes` agrees up front, which is also what you want in CI; without a
96
+ terminal and without `--yes` the command stops and says so).
97
+
98
+ ## Installation
99
+
100
+ ```bash
101
+ npm install -g @macrulez/mediatoolz
102
+ ```
103
+
104
+ Or run it without installing:
105
+
106
+ ```bash
107
+ npx @macrulez/mediatoolz image-batch ./photos -r -o ./out -f webp
108
+ ```
109
+
110
+ ## Quick start
111
+
112
+ ```bash
113
+ mediatoolz image-hash public/img # blurhash + thumbhash of every image, as JSON on stdout
114
+ mediatoolz image-hash public/img -r -t hazehash --budget 24 -o hashes.json # hazehash of 24 bytes at most
115
+ mediatoolz image-hash a.jpg,b.png,photos -r -t blurhash -f csv -o hashes.csv
116
+ mediatoolz image-hash public/img -r -f plain --per-file # photo.jpg.blurhash.txt + photo.jpg.thumbhash.txt
117
+ mediatoolz image-hash public/img -r -f ts -t thumbhash -o src/placeholders.ts --name placeholders
118
+ mediatoolz image-hash public/img -r --dry-run # write nothing, show a table of the hashes
119
+ mediatoolz image-hash public/img -r -t all --components auto -o hashes.json --cache --update --prune
120
+ mediatoolz image-hash public/img -r -o hashes.json --check # CI: exit 1 if hashes.json is stale
121
+
122
+ mediatoolz image-batch ./src/images -r -o ./public/images -w 400,800,1200 -f avif,webp,jpg -q high
123
+ mediatoolz image-batch ./photos -r -o ./out -c web # apply the saved config "web"
124
+ mediatoolz image-batch ./photos -r -o ./out -c web --select # tick the files from a list first
125
+ mediatoolz image-batch ./photos -r -o ./out -c web --dry-run # show what would be written
126
+ mediatoolz image-batch ./photos -r -o ./out -c web --hazehash --budget 20 --emit ./out/images.json
127
+ mediatoolz image-batch ./photos -r -o ./out -f webp # only change the format, keep the size
128
+ mediatoolz image-batch ./photos -r -o ./out --long 1600 -f webp,jpg --max-size 200KB # by long side, capped weight
129
+ mediatoolz image-batch ./assets -r --replace -w 1600 --dry-run # preview shrinking originals in place
130
+ mediatoolz image-batch ./assets -r --replace -w 1600 # do it: backup first, asks to confirm
131
+ mediatoolz image-batch restore .image-batch-backup/20261007-233706 # put the originals back
132
+ mediatoolz image-batch ./photos -r --beside -w 400 -f webp # results next to each source
133
+ mediatoolz image-batch init # create a config with a few questions
134
+ mediatoolz image-batch config # pick a config: apply, show, edit, copy, delete
135
+ mediatoolz image-batch sharpen new # save a sharpening preset, then --sharpen <name>
136
+
137
+ mediatoolz ui # web interface for both commands
138
+ mediatoolz ui --no-open --port 4477 # just print the address
139
+ ```
140
+
141
+ Every command has built-in `--help` — `mediatoolz --help` lists both commands
142
+ with their own flags inline; `mediatoolz <command> --help` for one command's
143
+ full flag list with defaults.
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ npm install
149
+ npm run build # tsc -> dist/, and the web interface
150
+ npm test # vitest
151
+ npm run lint # eslint .
152
+ npm run format # prettier --check .
153
+ npm run typecheck # tsc --noEmit
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Documentation & links
159
+
160
+ - 📖 **Full documentation:** [npm.vuecraft.ru/en/packages/mediatoolz](https://npm.vuecraft.ru/en/packages/mediatoolz/guide/overview.html)
161
+ - 🌐 **VueCraft:** [vuecraft.ru/en](https://vuecraft.ru/en)
162
+ - 👤 **Author:** [macrulez.ru/en](https://macrulez.ru/en)
163
+ - 💻 **GitHub:** [macrulezru/mediatoolz](https://github.com/macrulezru/mediatoolz)
164
+ - 📦 **NPM:** [@macrulez/mediatoolz](https://www.npmjs.com/package/@macrulez/mediatoolz)
165
+ - 🐛 **Issues:** [github.com/macrulezru/mediatoolz/issues](https://github.com/macrulezru/mediatoolz/issues)
166
+
167
+ ---
168
+
169
+ ## License
170
+
171
+ MIT
172
+
173
+ ---
174
+
175
+ ## 💖 Support the project
176
+
177
+ Open source takes time and effort. If this library saves you time or brings value, consider supporting further development.
178
+
179
+ <a href="https://donate.cryptocloud.plus/M6O34NIN" target="_blank">
180
+ <img src="https://img.shields.io/badge/Donate-CryptoCloud-8A2BE2?style=for-the-badge&logo=cryptocurrency&logoColor=white" alt="Donate via CryptoCloud">
181
+ </a>
182
+
183
+ Thank you for being part of this journey. ❤️
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'node:fs';
4
+ import { resolve } from 'node:path';
5
+ import { runImageHash } from './commands/image-hash/run.js';
6
+ import { renderImageHashReport } from './commands/image-hash/report.js';
7
+ import { DEFAULT_CACHE_FILE, DEFAULT_HAZEHASH_BUDGET, DEFAULT_IMAGE_EXTENSIONS, ImageHashUsageError, MAX_HAZEHASH_BUDGET, MIN_HAZEHASH_BUDGET, OUTPUT_FORMATS, parseBudget, parseComponents, parseExtensions, parseFileList, parseFormat, parseMaxPixels, parseSampleSize, parseTypes, } from './commands/image-hash/core.js';
8
+ import { registerImageBatch } from './commands/image-batch/cli.js';
9
+ import { registerUi } from './commands/ui/cli.js';
10
+ import { createStyle } from './format/style.js';
11
+ function wrapText(text, width) {
12
+ const words = text.split(' ');
13
+ const lines = [];
14
+ let current = '';
15
+ for (const word of words) {
16
+ const candidate = current ? `${current} ${word}` : word;
17
+ if (current && candidate.length > width) {
18
+ lines.push(current);
19
+ current = word;
20
+ }
21
+ else {
22
+ current = candidate;
23
+ }
24
+ }
25
+ if (current)
26
+ lines.push(current);
27
+ return lines;
28
+ }
29
+ /**
30
+ * Commander's own one-line-per-command "Commands:" list puts a command's
31
+ * own flags inline after its name (`image-hash [options]`) and leaves
32
+ * their descriptions for a separate `<command> --help` run — fine for a
33
+ * CLI with one or two flags per command, but it means the top-level
34
+ * `--help` never actually shows what any flag does. This builds a
35
+ * two-level list instead (command, then each of its own options indented
36
+ * below it with its own description), used in place of commander's
37
+ * default via `configureHelp({ visibleCommands: () => [] })` +
38
+ * `addHelpText('after')` — same technique as lintsync's/polyrepo-cli's
39
+ * own `formatCommandsHelp`.
40
+ *
41
+ * A flag present on EVERY command, with the exact same `flags` string and
42
+ * description (not just the same name coincidentally meaning something
43
+ * slightly different per command), is pulled out into a "Common options"
44
+ * block instead of being repeated under each one. A flag shared by only
45
+ * SOME commands stays listed under each of those commands directly —
46
+ * tried hoisting those into their own labeled sub-group too, but it's
47
+ * harder to keep in your head which commands a "Common options (a, b)"
48
+ * block actually applies to than to just see the flag where it's used.
49
+ * This only changes how the help text is RENDERED — every flag is still
50
+ * fully declared on its own command for real parsing, so
51
+ * `mediatoolz <command> --cwd x` keeps working exactly as before; nothing
52
+ * here is a true commander-level global option.
53
+ */
54
+ function formatCommandsHelp(commands) {
55
+ const real = commands.filter((cmd) => cmd.name() !== 'help');
56
+ // Which commands (by name) share the exact same flags+description for
57
+ // a given option, keyed on that pair so two different flags spelled
58
+ // the same but described differently never get merged.
59
+ const owners = new Map();
60
+ for (const cmd of real) {
61
+ for (const opt of cmd.options) {
62
+ const key = `${opt.flags}\u0000${opt.description}`;
63
+ const entry = owners.get(key) ?? { flags: opt.flags, desc: opt.description, commandCount: 0 };
64
+ entry.commandCount++;
65
+ owners.set(key, entry);
66
+ }
67
+ }
68
+ const universalRows = [];
69
+ const hoistedKeys = new Set();
70
+ for (const [key, entry] of owners) {
71
+ if (entry.commandCount !== real.length)
72
+ continue; // not on every command — stays with each one below
73
+ universalRows.push({ indent: 1, label: entry.flags, desc: entry.desc });
74
+ hoistedKeys.add(key);
75
+ }
76
+ const commandGroups = real.map((cmd) => {
77
+ const aliases = cmd.aliases();
78
+ const label = aliases.length > 0 ? `${cmd.name()}, ${aliases.join(', ')}` : cmd.name();
79
+ const ownRows = cmd.options
80
+ .filter((opt) => !hoistedKeys.has(`${opt.flags}\u0000${opt.description}`))
81
+ .map((opt) => ({ indent: 1, label: opt.flags, desc: opt.description }));
82
+ return [{ indent: 0, label, desc: cmd.description() }, ...ownRows];
83
+ });
84
+ const allRows = [universalRows, ...commandGroups].flat();
85
+ const labelWidth = Math.max(...allRows.map((r) => r.indent * 4 + r.label.length));
86
+ const totalWidth = (process.stdout.isTTY && process.stdout.columns) || 96;
87
+ const descWidth = Math.max(totalWidth - (2 + labelWidth + 2), 30);
88
+ function renderRow(row, lines) {
89
+ const fullLabel = ' '.repeat(row.indent * 4) + row.label;
90
+ const [firstLine, ...restLines] = wrapText(row.desc, descWidth);
91
+ lines.push(` ${fullLabel.padEnd(labelWidth)} ${firstLine ?? ''}`);
92
+ for (const cont of restLines) {
93
+ lines.push(` ${' '.repeat(labelWidth)} ${cont}`);
94
+ }
95
+ }
96
+ const lines = [];
97
+ if (universalRows.length > 0) {
98
+ lines.push('Common options (all commands):');
99
+ for (const row of universalRows)
100
+ renderRow(row, lines);
101
+ lines.push('');
102
+ }
103
+ lines.push('Commands:');
104
+ commandGroups.forEach((group, i) => {
105
+ if (i > 0)
106
+ lines.push('');
107
+ for (const row of group)
108
+ renderRow(row, lines);
109
+ });
110
+ return lines.join('\n');
111
+ }
112
+ const program = new Command();
113
+ program.enablePositionalOptions();
114
+ program
115
+ .name('mediatoolz')
116
+ .description('A small toolbox of CLI commands for images: resize, convert and recompress them in bulk, and generate placeholders')
117
+ // Hide commander's own one-line-per-command list (see formatCommandsHelp
118
+ // above for why) — the "after" text below replaces it with the
119
+ // two-level version instead of showing both.
120
+ .configureHelp({ visibleCommands: () => [] })
121
+ .addHelpText('after', () => `\n${formatCommandsHelp(program.commands)}`);
122
+ program
123
+ .command('image-hash')
124
+ .description('Generate hazehash/blurhash/thumbhash placeholders (and dominant color, tiny preview) for raster images — to stdout, one file, or a file per image')
125
+ .argument('[paths...]', 'image files, directories and http(s) URLs (several allowed, comma-separated too)', [])
126
+ .option('--cwd <path>', 'root paths are resolved against', process.cwd())
127
+ .option('-r, --recursive', 'also walk subdirectories of every given directory', false)
128
+ .option('--files-from <file>', 'read more paths/URLs from a file, one per line (- = stdin, # starts a comment)')
129
+ .option('--ext <list>', 'comma-separated image extensions to pick up from directories', DEFAULT_IMAGE_EXTENSIONS.join(','))
130
+ .option('--ignore <glob>', 'extra ignore pattern (repeatable), on top of the built-in defaults', (val, prev) => [...prev, val], [])
131
+ .option('--no-respect-gitignore', "don't also honor the project's .gitignore")
132
+ .option('-t, --type <list>', 'what to generate, comma-separated: hazehash, blurhash, thumbhash, color (dominant), preview (tiny PNG data URI); both = blurhash+thumbhash, all = everything', 'both')
133
+ .option('--budget <bytes>', `hazehash: maximum size of one hash in bytes, header included, ${MIN_HAZEHASH_BUDGET}-${MAX_HAZEHASH_BUDGET} (16-48 is the tuned range)`, String(DEFAULT_HAZEHASH_BUDGET))
134
+ .option('--components <XxY|auto>', 'blurhash components, each side 1-9, or auto to pick them by aspect ratio', '4x3')
135
+ .option('--size <px>', 'longest side the image is scaled down to before hashing (1-100)', '100')
136
+ .option('--max-pixels <n>', 'refuse images with more pixels than this (0 = no limit; default: sharp limit, ~268 million)')
137
+ .option('-f, --format <format>', `format of the generated hashes: ${OUTPUT_FORMATS.join(', ')} (plain = just the hash text); --json prints the full report instead`, 'json')
138
+ .option('--name <identifier>', 'exported constant name for --format ts/js', 'imageHashes')
139
+ .option('--key-base <dir>', 'make the file keys in the output relative to this directory')
140
+ .option('--key-prefix <text>', 'text put in front of every file key, e.g. / for URL-style keys')
141
+ .option('-o, --out <file>', 'write everything into one file instead of stdout')
142
+ .option('--update', 'merge into the existing -o file instead of replacing it', false)
143
+ .option('--prune', 'with --update, drop entries whose image no longer exists', false)
144
+ .option('--per-file', 'write one file per image, next to the image', false)
145
+ .option('--out-dir <dir>', 'write the per-image files into this directory (implies --per-file)')
146
+ .option('--suffix <text>', 'per-image file name suffix after the image file name, {type} = hazehash/blurhash/thumbhash/hash (default: .{type})')
147
+ .option('--out-ext <ext>', 'per-image file extension (default: by --format, plain = .txt)')
148
+ .option('--check', 'write nothing; exit 1 if the output files are missing or differ from the images', false)
149
+ .option('--cache [file]', `skip images that did not change since the last run, remembered in this file (default: ${DEFAULT_CACHE_FILE})`)
150
+ .option('--concurrency <n>', 'images processed in parallel', '4')
151
+ .option('--dry-run', 'write nothing — show the result as a table instead', false)
152
+ .option('-y, --yes', 'install the missing "sharp" image library without asking', false)
153
+ .option('--json', 'machine-readable output', false)
154
+ .option('--quiet', 'suppress output when there is nothing to report', false)
155
+ .option('--plain', 'disable color/banner/celebration copy, even in a real terminal', false)
156
+ .option('--color', 'force colored output even when piped or in CI', false)
157
+ .action(async (paths, options) => {
158
+ const style = createStyle({ plain: options.plain, color: options.color });
159
+ const showProgress = !options.json && !options.quiet && Boolean(process.stderr.isTTY) && !options.plain;
160
+ let progressShown = false;
161
+ try {
162
+ const listed = options.filesFrom !== undefined
163
+ ? parseFileList(readFileSync(options.filesFrom === '-' ? 0 : options.filesFrom, 'utf8'))
164
+ : [];
165
+ const report = await runImageHash({
166
+ paths: [...paths, ...listed],
167
+ cwd: resolve(options.cwd),
168
+ types: parseTypes(options.type),
169
+ recursive: options.recursive,
170
+ extensions: parseExtensions(options.ext),
171
+ ignoreGlobs: options.ignore,
172
+ respectGitignore: options.respectGitignore,
173
+ components: parseComponents(options.components),
174
+ budget: parseBudget(options.budget),
175
+ size: parseSampleSize(options.size),
176
+ ...(options.maxPixels !== undefined
177
+ ? { maxPixels: parseMaxPixels(options.maxPixels) }
178
+ : {}),
179
+ format: parseFormat(options.format),
180
+ exportName: options.name,
181
+ ...(options.keyBase !== undefined ? { keyBase: options.keyBase } : {}),
182
+ ...(options.keyPrefix !== undefined ? { keyPrefix: options.keyPrefix } : {}),
183
+ ...(options.out !== undefined ? { out: options.out } : {}),
184
+ update: options.update,
185
+ prune: options.prune,
186
+ perFile: options.perFile,
187
+ ...(options.outDir !== undefined ? { outDir: options.outDir } : {}),
188
+ ...(options.suffix !== undefined ? { suffix: options.suffix } : {}),
189
+ ...(options.outExt !== undefined ? { outExtension: options.outExt } : {}),
190
+ check: options.check,
191
+ ...(options.cache !== undefined
192
+ ? { cache: options.cache === true ? DEFAULT_CACHE_FILE : String(options.cache) }
193
+ : {}),
194
+ concurrency: Math.max(1, Number.parseInt(options.concurrency, 10) || 4),
195
+ assumeYes: options.yes,
196
+ dryRun: options.dryRun,
197
+ ...(showProgress
198
+ ? {
199
+ onProgress: ({ done, total }) => {
200
+ if (total < 2)
201
+ return;
202
+ progressShown = true;
203
+ process.stderr.write(`\r${style.info(`Hashing ${done}/${total}`)}`);
204
+ },
205
+ }
206
+ : {}),
207
+ });
208
+ if (progressShown)
209
+ process.stderr.write('\r\x1b[K');
210
+ if (options.json) {
211
+ console.log(JSON.stringify(report, null, 2));
212
+ }
213
+ else if (report.stdout !== null) {
214
+ process.stdout.write(report.stdout);
215
+ for (const error of report.errors)
216
+ console.error(`${error.file} — ${error.message}`);
217
+ }
218
+ else {
219
+ const text = renderImageHashReport(report, {
220
+ quiet: options.quiet,
221
+ plain: options.plain,
222
+ color: options.color,
223
+ });
224
+ if (text)
225
+ console.log(text);
226
+ }
227
+ process.exitCode = report.exitCode;
228
+ }
229
+ catch (error) {
230
+ if (progressShown)
231
+ process.stderr.write('\r\x1b[K');
232
+ if (error instanceof ImageHashUsageError) {
233
+ console.error(`error: ${error.message}`);
234
+ process.exitCode = 2;
235
+ return;
236
+ }
237
+ throw error;
238
+ }
239
+ });
240
+ registerImageBatch(program);
241
+ registerUi(program);
242
+ program.parse(process.argv);
243
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare const DEFAULT_BACKUP_DIR = ".image-batch-backup";
2
+ export declare const JOURNAL_FILE = "journal.json";
3
+ export interface JournalEntry {
4
+ path: string;
5
+ backup: string;
6
+ bytesBefore: number;
7
+ bytesAfter: number;
8
+ hashBefore: string;
9
+ hashAfter: string;
10
+ }
11
+ export interface Journal {
12
+ version: 1;
13
+ createdAt: string;
14
+ cwd: string;
15
+ entries: JournalEntry[];
16
+ }
17
+ export declare function backupDirFor(cwd: string, base: string | undefined, now: Date): string;
18
+ export declare function backupPathFor(backupDir: string, cwd: string, absolute: string): string;
19
+ export declare function copyIntoBackup(source: string, backup: string): void;
20
+ export declare function newJournal(cwd: string, now: Date): Journal;
21
+ export declare function saveJournal(backupDir: string, journal: Journal): void;
22
+ export declare function loadJournal(backupDir: string): Journal;
@@ -0,0 +1,55 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
3
+ import { ImageBatchUsageError } from './errors.js';
4
+ export const DEFAULT_BACKUP_DIR = '.image-batch-backup';
5
+ export const JOURNAL_FILE = 'journal.json';
6
+ function stamp(date) {
7
+ const pad = (n) => String(n).padStart(2, '0');
8
+ return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
9
+ }
10
+ export function backupDirFor(cwd, base, now) {
11
+ const root = resolve(cwd, base ?? DEFAULT_BACKUP_DIR);
12
+ let dir = join(root, stamp(now));
13
+ let suffix = 1;
14
+ while (existsSync(dir)) {
15
+ suffix += 1;
16
+ dir = join(root, `${stamp(now)}-${suffix}`);
17
+ }
18
+ return dir;
19
+ }
20
+ export function backupPathFor(backupDir, cwd, absolute) {
21
+ const rel = relative(cwd, absolute);
22
+ if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) {
23
+ return join(backupDir, '_outside', absolute.replace(/[:\\/]+/g, '_'));
24
+ }
25
+ return join(backupDir, rel);
26
+ }
27
+ export function copyIntoBackup(source, backup) {
28
+ mkdirSync(dirname(backup), { recursive: true });
29
+ copyFileSync(source, backup);
30
+ }
31
+ export function newJournal(cwd, now) {
32
+ return { version: 1, createdAt: now.toISOString(), cwd, entries: [] };
33
+ }
34
+ export function saveJournal(backupDir, journal) {
35
+ mkdirSync(backupDir, { recursive: true });
36
+ writeFileSync(join(backupDir, JOURNAL_FILE), `${JSON.stringify(journal, null, 2)}\n`);
37
+ }
38
+ export function loadJournal(backupDir) {
39
+ const path = join(backupDir, JOURNAL_FILE);
40
+ if (!existsSync(path)) {
41
+ throw new ImageBatchUsageError(`${backupDir} has no ${JOURNAL_FILE} — pass the dated backup folder that --replace created`);
42
+ }
43
+ let parsed;
44
+ try {
45
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
46
+ }
47
+ catch (error) {
48
+ throw new ImageBatchUsageError(`${path}: not valid JSON — ${error instanceof Error ? error.message : String(error)}`);
49
+ }
50
+ if (parsed.version !== 1 || !Array.isArray(parsed.entries)) {
51
+ throw new ImageBatchUsageError(`${path}: not a journal written by image-batch`);
52
+ }
53
+ return parsed;
54
+ }
55
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../../src/commands/image-batch/backup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export interface BatchCacheEntry {
2
+ fingerprint: string;
3
+ bytes: number;
4
+ settingsKey?: string;
5
+ hash?: string;
6
+ }
7
+ export interface BatchCache {
8
+ version: 1;
9
+ outputs: Record<string, BatchCacheEntry>;
10
+ }
11
+ export declare function emptyBatchCache(): BatchCache;
12
+ export declare function cacheKeyFor(placement: 'out' | 'beside' | 'replace', target: string, roots: string[]): string;
13
+ export declare function defaultCachePath(key: string, baseDir?: string): string;
14
+ export declare function loadBatchCache(path: string): BatchCache;
15
+ export declare function saveBatchCache(path: string, cache: BatchCache): void;
@@ -0,0 +1,38 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ export function emptyBatchCache() {
6
+ return { version: 1, outputs: {} };
7
+ }
8
+ export function cacheKeyFor(placement, target, roots) {
9
+ if (placement === 'out')
10
+ return resolve(target);
11
+ return `${placement}:${roots
12
+ .map((root) => resolve(root))
13
+ .sort()
14
+ .join('|')}`;
15
+ }
16
+ export function defaultCachePath(key, baseDir = homedir()) {
17
+ const id = createHash('sha1').update(key.toLowerCase()).digest('hex').slice(0, 16);
18
+ return join(baseDir, '.mediatoolz', 'cache', 'image-batch', `${id}.json`);
19
+ }
20
+ export function loadBatchCache(path) {
21
+ if (!existsSync(path))
22
+ return emptyBatchCache();
23
+ try {
24
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
25
+ if (parsed.version !== 1 || typeof parsed.outputs !== 'object' || parsed.outputs === null) {
26
+ return emptyBatchCache();
27
+ }
28
+ return parsed;
29
+ }
30
+ catch {
31
+ return emptyBatchCache();
32
+ }
33
+ }
34
+ export function saveBatchCache(path, cache) {
35
+ mkdirSync(dirname(path), { recursive: true });
36
+ writeFileSync(path, `${JSON.stringify(cache)}\n`);
37
+ }
38
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../src/commands/image-batch/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAclD,MAAM,UAAU,eAAe;IAC7B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;AACpC,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,SAAuC,EACvC,MAAc,EACd,KAAe;IAEf,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/C,OAAO,GAAG,SAAS,IAAI,KAAK;SACzB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;SAC5B,IAAI,EAAE;SACN,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,UAAkB,OAAO,EAAE;IACvE,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAClF,OAAO,IAAI,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;AAC3E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,eAAe,EAAE,CAAA;IAC/C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAe,CAAA;QACnE,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC1F,OAAO,eAAe,EAAE,CAAA;QAC1B,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,EAAE,CAAA;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAiB;IAC5D,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACnD,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Command } from 'commander';
2
+ import { type BatchConfig } from './config.js';
3
+ export declare function chooseConfig(options: {
4
+ config?: string;
5
+ cwd: string;
6
+ adHoc: boolean;
7
+ interactive: boolean;
8
+ }, style: {
9
+ plain?: boolean;
10
+ color?: boolean;
11
+ }): Promise<{
12
+ config: BatchConfig;
13
+ label?: string;
14
+ }>;
15
+ export declare function registerImageBatch(program: Command): void;