From 139ba8ee167109f1da5846713d7c2e99a3ec0436 Mon Sep 17 00:00:00 2001 From: geoffsee <> Date: Wed, 2 Jul 2025 18:09:21 -0400 Subject: [PATCH] init --- .gitignore | 4 + README.md | 200 ++++++++++++ deno.json | 9 + deno.lock | 639 +++++++++++++++++++++++++++++++++++++ imjen.example.config.jsonc | 32 ++ package.json | 16 + src/default_config.ts | 43 +++ src/imjen.ts | 152 +++++++++ src/main.ts | 13 + src/main_test.ts | 0 10 files changed, 1108 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 deno.json create mode 100644 deno.lock create mode 100644 imjen.example.config.jsonc create mode 100644 package.json create mode 100644 src/default_config.ts create mode 100644 src/imjen.ts create mode 100755 src/main.ts create mode 100644 src/main_test.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c83478e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/imjen.config.jsonc +/.idea/ +/dist/ +/node_modules/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..83c0cb3 --- /dev/null +++ b/README.md @@ -0,0 +1,200 @@ +# Imjen - AI Image Generator + +Imjen is a command-line tool for generating AI-powered images using OpenAI's DALL-E 3 API. It's optimized for creating minimalistic flat vector icons and other images with customizable prompts and dimensions. + +## Quick Start with npx + +You can run Imjen directly without installation using npx: + +```bash +npx imjen +``` + +## Prerequisites + +- **OpenAI API Key**: You need an OpenAI API key to use this tool. Get one at [platform.openai.com](https://platform.openai.com) +- **Node.js**: Required for npx execution + +## Setup + +1. **Set your API key** (choose one method): + - Environment variable: `export OPENAI_API_KEY=your_key_here` + - Or add it to your configuration file (see Configuration section) + +2. **Run the tool**: + ```bash + npx imjen + ``` + +On first run, Imjen will automatically create a configuration file (`imjen.config.jsonc`) with example settings. + +## Configuration + +Imjen uses a `imjen.config.jsonc` file for configuration. Here's the structure: + +```jsonc +{ + // Optional: Set API key in config instead of environment + // "OPENAI_API_KEY": "your_key_here", + + // Default dimensions (optional) + "width": 1024, + "height": 1024, + + // Default prompt template (optional) + "promptTemplate": "ui-icon", + + // Generate multiple images + "images": [ + { + "description": "Compass Rose for navigation", + "promptArg": "minimal flat compass rose icon", + "width": 512, + "height": 512 + }, + { + "description": "Wind Direction Arrow", + "promptTemplate": "flat-icon", + "width": 256, + "height": 256 + } + ] +} +``` + +### Configuration Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `OPENAI_API_KEY` | string | - | Your OpenAI API key | +| `width` | number | `1024` | Default image width in pixels | +| `height` | number | `1024` | Default image height in pixels | +| `promptTemplate` | string | `"realistic"` | Default prompt template to use | +| `description` | string | - | Description for the image | +| `promptArg` | string | - | Custom prompt (overrides template) | +| `images` | array | - | Array of image configurations for batch generation | + +### Available Prompt Templates + +| Template | Description | +|----------|-------------| +| `ui-icon` | Minimalist UI icon, clean design, simple shapes, flat style | +| `flat-icon` | Flat icon design, simple geometric shapes, solid colors | +| `outline-icon` | Outline icon, line art style, minimal design | +| `realistic` | Photorealistic image, high quality, detailed | +| `artistic` | Artistic interpretation, creative style, expressive | + +## Usage Examples + +### Single Image Generation + +For single image generation, configure a single image in the images array: + +```jsonc +{ + "width": 512, + "height": 512, + "images": [ + { + "description": "My custom icon", + "promptArg": "minimalist settings gear icon" + } + ] +} +``` + +### Multiple Images (Batch Generation) + +Use the `images` array for generating multiple images: + +```jsonc +{ + "width": 1024, + "height": 1024, + "promptTemplate": "ui-icon", + "images": [ + { + "description": "Home Icon", + "promptTemplate": "flat-icon" + }, + { + "description": "User Profile Icon", + "promptArg": "minimal user profile icon", + "width": 512, + "height": 512 + }, + { + "description": "Search Icon", + "promptTemplate": "outline-icon" + } + ] +} +``` + +### Environment Variables + +Set your API key as an environment variable: + +```bash +# Linux/macOS +export OPENAI_API_KEY=your_key_here +npx imjen + +# Windows +set OPENAI_API_KEY=your_key_here +npx imjen +``` + +## Prompt Templates + +If no `promptArg` is specified, Imjen uses prompt templates to generate images. The default template is "realistic", but you can specify different templates: + +- **ui-icon**: Creates minimalist UI icons with clean design and simple shapes +- **flat-icon**: Generates flat icon designs with geometric shapes and solid colors +- **outline-icon**: Produces outline-style icons with line art and minimal design +- **realistic**: Creates photorealistic, high-quality, detailed images +- **artistic**: Generates artistic interpretations with creative and expressive styles + +Templates automatically incorporate your description into optimized prompts for DALL-E 3. + +## Output + +- Generated images are saved as PNG files in the current directory +- Files are automatically named using the format: `01_description.png`, `02_description.png`, etc. +- Filenames are based on the image description, sanitized for filesystem compatibility +- Images are automatically resized to the specified dimensions using high-quality algorithms +- Success/error messages are displayed in the console + +## Troubleshooting + +### Common Issues + +1. **"OPENAI_API_KEY is not set"** + - Set the API key as an environment variable or in your config file + - Get an API key from [platform.openai.com](https://platform.openai.com) + +2. **"Error generating image"** + - Check your internet connection + - Verify your API key is valid and has sufficient credits + - Ensure your OpenAI account has access to DALL-E 3 + +3. **Configuration file not found** + - Run `npx imjen` once to auto-generate the example configuration + - Manually create `imjen.config.jsonc` in your project directory + +## Model + +Imjen uses OpenAI's DALL-E 3 model for all image generation. DALL-E 3 provides: + +- High-quality image generation with excellent prompt adherence +- Support for various artistic styles and photorealistic images +- Built-in safety features and content filtering +- Consistent results optimized for creative applications + +## Contributing + +This project is built with Deno and TypeScript. The source code is available on the project repository. + +## License + +See the project repository for license information. diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..eb32956 --- /dev/null +++ b/deno.json @@ -0,0 +1,9 @@ +{ + "tasks": { + "build": "(cd src && deno compile --allow-all --no-check --output ../dist/imjen ./main.ts) && cp imjen.example.config.jsonc dist/imjen.example.config.jsonc" + }, + "imports": { + "@openai/openai": "jsr:@openai/openai@^5.8.2", + "@std/assert": "jsr:@std/assert@1" + } +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..a9299c6 --- /dev/null +++ b/deno.lock @@ -0,0 +1,639 @@ +{ + "version": "5", + "specifiers": { + "jsr:@openai/openai@*": "5.8.2", + "jsr:@openai/openai@^5.8.2": "5.8.2", + "jsr:@std/assert@1": "1.0.13", + "jsr:@std/internal@^1.0.6": "1.0.9", + "npm:jimp@^1.6.0": "1.6.0", + "npm:zod@3": "3.24.4" + }, + "jsr": { + "@openai/openai@5.8.2": { + "integrity": "d25f147f6e687962b87ca1acd392adb434ff2d8e2c1ad7013d245cb2a4c6a663", + "dependencies": [ + "npm:zod" + ] + }, + "@std/assert@1.0.13": { + "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", + "dependencies": [ + "jsr:@std/internal" + ] + }, + "@std/internal@1.0.9": { + "integrity": "bdfb97f83e4db7a13e8faab26fb1958d1b80cc64366501af78a0aee151696eb8" + } + }, + "npm": { + "@jimp/core@1.6.0": { + "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", + "dependencies": [ + "@jimp/file-ops", + "@jimp/types", + "@jimp/utils", + "await-to-js", + "exif-parser", + "file-type", + "mime" + ] + }, + "@jimp/diff@1.6.0": { + "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", + "dependencies": [ + "@jimp/plugin-resize", + "@jimp/types", + "@jimp/utils", + "pixelmatch" + ] + }, + "@jimp/file-ops@1.6.0": { + "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==" + }, + "@jimp/js-bmp@1.6.0": { + "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "@jimp/utils", + "bmp-ts" + ] + }, + "@jimp/js-gif@1.6.0": { + "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "gifwrap", + "omggif" + ] + }, + "@jimp/js-jpeg@1.6.0": { + "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "jpeg-js" + ] + }, + "@jimp/js-png@1.6.0": { + "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "pngjs@7.0.0" + ] + }, + "@jimp/js-tiff@1.6.0": { + "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "utif2" + ] + }, + "@jimp/plugin-blit@1.6.0": { + "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "dependencies": [ + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/plugin-blur@1.6.0": { + "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "dependencies": [ + "@jimp/core", + "@jimp/utils" + ] + }, + "@jimp/plugin-circle@1.6.0": { + "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "dependencies": [ + "@jimp/types", + "zod" + ] + }, + "@jimp/plugin-color@1.6.0": { + "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "@jimp/utils", + "tinycolor2", + "zod" + ] + }, + "@jimp/plugin-contain@1.6.0": { + "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "dependencies": [ + "@jimp/core", + "@jimp/plugin-blit", + "@jimp/plugin-resize", + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/plugin-cover@1.6.0": { + "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "dependencies": [ + "@jimp/core", + "@jimp/plugin-crop", + "@jimp/plugin-resize", + "@jimp/types", + "zod" + ] + }, + "@jimp/plugin-crop@1.6.0": { + "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/plugin-displace@1.6.0": { + "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "dependencies": [ + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/plugin-dither@1.6.0": { + "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "dependencies": [ + "@jimp/types" + ] + }, + "@jimp/plugin-fisheye@1.6.0": { + "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "dependencies": [ + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/plugin-flip@1.6.0": { + "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "dependencies": [ + "@jimp/types", + "zod" + ] + }, + "@jimp/plugin-hash@1.6.0": { + "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", + "dependencies": [ + "@jimp/core", + "@jimp/js-bmp", + "@jimp/js-jpeg", + "@jimp/js-png", + "@jimp/js-tiff", + "@jimp/plugin-color", + "@jimp/plugin-resize", + "@jimp/types", + "@jimp/utils", + "any-base" + ] + }, + "@jimp/plugin-mask@1.6.0": { + "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "dependencies": [ + "@jimp/types", + "zod" + ] + }, + "@jimp/plugin-print@1.6.0": { + "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "dependencies": [ + "@jimp/core", + "@jimp/js-jpeg", + "@jimp/js-png", + "@jimp/plugin-blit", + "@jimp/types", + "parse-bmfont-ascii", + "parse-bmfont-binary", + "parse-bmfont-xml", + "simple-xml-to-json", + "zod" + ] + }, + "@jimp/plugin-quantize@1.6.0": { + "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "dependencies": [ + "image-q", + "zod" + ] + }, + "@jimp/plugin-resize@1.6.0": { + "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", + "dependencies": [ + "@jimp/core", + "@jimp/types", + "zod" + ] + }, + "@jimp/plugin-rotate@1.6.0": { + "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", + "dependencies": [ + "@jimp/core", + "@jimp/plugin-crop", + "@jimp/plugin-resize", + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/plugin-threshold@1.6.0": { + "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", + "dependencies": [ + "@jimp/core", + "@jimp/plugin-color", + "@jimp/plugin-hash", + "@jimp/types", + "@jimp/utils", + "zod" + ] + }, + "@jimp/types@1.6.0": { + "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", + "dependencies": [ + "zod" + ] + }, + "@jimp/utils@1.6.0": { + "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", + "dependencies": [ + "@jimp/types", + "tinycolor2" + ] + }, + "@tokenizer/token@0.3.0": { + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "@types/node@16.9.1": { + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" + }, + "abort-controller@3.0.0": { + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": [ + "event-target-shim" + ] + }, + "any-base@1.1.0": { + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==" + }, + "await-to-js@3.0.0": { + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==" + }, + "base64-js@1.5.1": { + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bmp-ts@1.0.9": { + "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==" + }, + "buffer@6.0.3": { + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dependencies": [ + "base64-js", + "ieee754" + ] + }, + "event-target-shim@5.0.1": { + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, + "events@3.3.0": { + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "exif-parser@0.1.12": { + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==" + }, + "file-type@16.5.4": { + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "dependencies": [ + "readable-web-to-node-stream", + "strtok3", + "token-types" + ] + }, + "gifwrap@0.10.1": { + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "dependencies": [ + "image-q", + "omggif" + ] + }, + "ieee754@1.2.1": { + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "image-q@4.0.0": { + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "dependencies": [ + "@types/node" + ] + }, + "jimp@1.6.0": { + "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", + "dependencies": [ + "@jimp/core", + "@jimp/diff", + "@jimp/js-bmp", + "@jimp/js-gif", + "@jimp/js-jpeg", + "@jimp/js-png", + "@jimp/js-tiff", + "@jimp/plugin-blit", + "@jimp/plugin-blur", + "@jimp/plugin-circle", + "@jimp/plugin-color", + "@jimp/plugin-contain", + "@jimp/plugin-cover", + "@jimp/plugin-crop", + "@jimp/plugin-displace", + "@jimp/plugin-dither", + "@jimp/plugin-fisheye", + "@jimp/plugin-flip", + "@jimp/plugin-hash", + "@jimp/plugin-mask", + "@jimp/plugin-print", + "@jimp/plugin-quantize", + "@jimp/plugin-resize", + "@jimp/plugin-rotate", + "@jimp/plugin-threshold", + "@jimp/types", + "@jimp/utils" + ] + }, + "jpeg-js@0.4.4": { + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" + }, + "mime@3.0.0": { + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "bin": true + }, + "omggif@1.0.10": { + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==" + }, + "pako@1.0.11": { + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parse-bmfont-ascii@1.0.6": { + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==" + }, + "parse-bmfont-binary@1.0.6": { + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==" + }, + "parse-bmfont-xml@1.1.6": { + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dependencies": [ + "xml-parse-from-string", + "xml2js" + ] + }, + "peek-readable@4.1.0": { + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==" + }, + "pixelmatch@5.3.0": { + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "dependencies": [ + "pngjs@6.0.0" + ], + "bin": true + }, + "pngjs@6.0.0": { + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==" + }, + "pngjs@7.0.0": { + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==" + }, + "process@0.11.10": { + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, + "readable-stream@4.7.0": { + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dependencies": [ + "abort-controller", + "buffer", + "events", + "process", + "string_decoder" + ] + }, + "readable-web-to-node-stream@3.0.4": { + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "dependencies": [ + "readable-stream" + ] + }, + "safe-buffer@5.2.1": { + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sax@1.4.1": { + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + }, + "simple-xml-to-json@1.2.3": { + "integrity": "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==" + }, + "string_decoder@1.3.0": { + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": [ + "safe-buffer" + ] + }, + "strtok3@6.3.0": { + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "dependencies": [ + "@tokenizer/token", + "peek-readable" + ] + }, + "tinycolor2@1.6.0": { + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, + "token-types@4.2.1": { + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "dependencies": [ + "@tokenizer/token", + "ieee754" + ] + }, + "utif2@4.1.0": { + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "dependencies": [ + "pako" + ] + }, + "xml-parse-from-string@1.0.1": { + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==" + }, + "xml2js@0.5.0": { + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": [ + "sax", + "xmlbuilder" + ] + }, + "xmlbuilder@11.0.1": { + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, + "zod@3.24.4": { + "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==" + } + }, + "redirects": { + "https://deno.land/std/fs/mod.ts": "https://deno.land/std@0.224.0/fs/mod.ts" + }, + "remote": { + "https://deno.land/std@0.204.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.204.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.204.0/json/common.ts": "ecd5e87d45b5f0df33238ed8b1746e1444da7f5c86ae53d0f0b04280f41a25bb", + "https://deno.land/std@0.204.0/jsonc/mod.ts": "b88dce28eb3645667caa856538ae2fe87af51410822544a0b45a4177ef3bd7dd", + "https://deno.land/std@0.204.0/jsonc/parse.ts": "c1096e2b7ffb4996d7ed841dfdb29a4fccc78edcc55299beaa20d6fe5facf7b6", + "https://deno.land/std@0.204.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", + "https://deno.land/std@0.204.0/path/_common/basename.ts": "0d978ff818f339cd3b1d09dc914881f4d15617432ae519c1b8fdc09ff8d3789a", + "https://deno.land/std@0.204.0/path/_common/common.ts": "9e4233b2eeb50f8b2ae10ecc2108f58583aea6fd3e8907827020282dc2b76143", + "https://deno.land/std@0.204.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.204.0/path/_common/dirname.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.204.0/path/_common/format.ts": "11aa62e316dfbf22c126917f5e03ea5fe2ee707386555a8f513d27ad5756cf96", + "https://deno.land/std@0.204.0/path/_common/from_file_url.ts": "ef1bf3197d2efbf0297a2bdbf3a61d804b18f2bcce45548ae112313ec5be3c22", + "https://deno.land/std@0.204.0/path/_common/glob_to_reg_exp.ts": "5c3c2b79fc2294ec803d102bd9855c451c150021f452046312819fbb6d4dc156", + "https://deno.land/std@0.204.0/path/_common/is_glob.ts": "567dce5c6656bdedfc6b3ee6c0833e1e4db2b8dff6e62148e94a917f289c06ad", + "https://deno.land/std@0.204.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", + "https://deno.land/std@0.204.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", + "https://deno.land/std@0.204.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4", + "https://deno.land/std@0.204.0/path/_common/strip_trailing_separators.ts": "7ffc7c287e97bdeeee31b155828686967f222cd73f9e5780bfe7dfb1b58c6c65", + "https://deno.land/std@0.204.0/path/_common/to_file_url.ts": "a8cdd1633bc9175b7eebd3613266d7c0b6ae0fb0cff24120b6092ac31662f9ae", + "https://deno.land/std@0.204.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.204.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", + "https://deno.land/std@0.204.0/path/basename.ts": "04bb5ef3e86bba8a35603b8f3b69537112cdd19ce64b77f2522006da2977a5f3", + "https://deno.land/std@0.204.0/path/common.ts": "f4d061c7d0b95a65c2a1a52439edec393e906b40f1caf4604c389fae7caa80f5", + "https://deno.land/std@0.204.0/path/dirname.ts": "88a0a71c21debafc4da7a4cd44fd32e899462df458fbca152390887d41c40361", + "https://deno.land/std@0.204.0/path/extname.ts": "2da4e2490f3b48b7121d19fb4c91681a5e11bd6bd99df4f6f47d7a71bb6ecdf2", + "https://deno.land/std@0.204.0/path/format.ts": "3457530cc85d1b4bab175f9ae73998b34fd456c830d01883169af0681b8894fb", + "https://deno.land/std@0.204.0/path/from_file_url.ts": "e7fa233ea1dff9641e8d566153a24d95010110185a6f418dd2e32320926043f8", + "https://deno.land/std@0.204.0/path/glob.ts": "9c77cf47db1d786e2ebf66670824d03fd84ecc7c807cac24441eb9d5cb6a2986", + "https://deno.land/std@0.204.0/path/is_absolute.ts": "67232b41b860571c5b7537f4954c88d86ae2ba45e883ee37d3dec27b74909d13", + "https://deno.land/std@0.204.0/path/join.ts": "98d3d76c819af4a11a81d5ba2dbb319f1ce9d63fc2b615597d4bcfddd4a89a09", + "https://deno.land/std@0.204.0/path/mod.ts": "2d62a0a8b78a60e8e6f485d881bac6b61d58573b11cf585fb7c8fc50d9b20d80", + "https://deno.land/std@0.204.0/path/normalize.ts": "aa95be9a92c7bd4f9dc0ba51e942a1973e2b93d266cd74f5ca751c136d520b66", + "https://deno.land/std@0.204.0/path/parse.ts": "d87ff0deef3fb495bc0d862278ff96da5a06acf0625ca27769fc52ac0d3d6ece", + "https://deno.land/std@0.204.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", + "https://deno.land/std@0.204.0/path/posix/basename.ts": "a630aeb8fd8e27356b1823b9dedd505e30085015407caa3396332752f6b8406a", + "https://deno.land/std@0.204.0/path/posix/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.204.0/path/posix/dirname.ts": "f48c9c42cc670803b505478b7ef162c7cfa9d8e751b59d278b2ec59470531472", + "https://deno.land/std@0.204.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0", + "https://deno.land/std@0.204.0/path/posix/format.ts": "b94876f77e61bfe1f147d5ccb46a920636cd3cef8be43df330f0052b03875968", + "https://deno.land/std@0.204.0/path/posix/from_file_url.ts": "b97287a83e6407ac27bdf3ab621db3fccbf1c27df0a1b1f20e1e1b5acf38a379", + "https://deno.land/std@0.204.0/path/posix/glob.ts": "86c3f06d1c98303613c74650961c3e24bdb871cde2a97c3ae7f0f6d4abbef445", + "https://deno.land/std@0.204.0/path/posix/is_absolute.ts": "159900a3422d11069d48395568217eb7fc105ceda2683d03d9b7c0f0769e01b8", + "https://deno.land/std@0.204.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", + "https://deno.land/std@0.204.0/path/posix/mod.ts": "6bfa8a42d85345b12dbe8571028ca2c62d460b6ef968125e498602b43b6cf6b6", + "https://deno.land/std@0.204.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", + "https://deno.land/std@0.204.0/path/posix/parse.ts": "199208f373dd93a792e9c585352bfc73a6293411bed6da6d3bc4f4ef90b04c8e", + "https://deno.land/std@0.204.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c", + "https://deno.land/std@0.204.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285", + "https://deno.land/std@0.204.0/path/posix/separator.ts": "0b6573b5f3269a3164d8edc9cefc33a02dd51003731c561008c8bb60220ebac1", + "https://deno.land/std@0.204.0/path/posix/to_file_url.ts": "08d43ea839ee75e9b8b1538376cfe95911070a655cd312bc9a00f88ef14967b6", + "https://deno.land/std@0.204.0/path/posix/to_namespaced_path.ts": "c9228a0e74fd37e76622cd7b142b8416663a9b87db643302fa0926b5a5c83bdc", + "https://deno.land/std@0.204.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c", + "https://deno.land/std@0.204.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867", + "https://deno.land/std@0.204.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", + "https://deno.land/std@0.204.0/path/to_file_url.ts": "edaafa089e0bce386e1b2d47afe7c72e379ff93b28a5829a5885e4b6c626d864", + "https://deno.land/std@0.204.0/path/to_namespaced_path.ts": "cf8734848aac3c7527d1689d2adf82132b1618eff3cc523a775068847416b22a", + "https://deno.land/std@0.204.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54", + "https://deno.land/std@0.204.0/path/windows/basename.ts": "8a9dbf7353d50afbc5b221af36c02a72c2d1b2b5b9f7c65bf6a5a2a0baf88ad3", + "https://deno.land/std@0.204.0/path/windows/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", + "https://deno.land/std@0.204.0/path/windows/dirname.ts": "5c2aa541384bf0bd9aca821275d2a8690e8238fa846198ef5c7515ce31a01a94", + "https://deno.land/std@0.204.0/path/windows/extname.ts": "07f4fa1b40d06a827446b3e3bcc8d619c5546b079b8ed0c77040bbef716c7614", + "https://deno.land/std@0.204.0/path/windows/format.ts": "343019130d78f172a5c49fdc7e64686a7faf41553268961e7b6c92a6d6548edf", + "https://deno.land/std@0.204.0/path/windows/from_file_url.ts": "d53335c12b0725893d768be3ac6bf0112cc5b639d2deb0171b35988493b46199", + "https://deno.land/std@0.204.0/path/windows/glob.ts": "0286fb89ecd21db5cbf3b6c79e2b87c889b03f1311e66fb769e6b905d4142332", + "https://deno.land/std@0.204.0/path/windows/is_absolute.ts": "245b56b5f355ede8664bd7f080c910a97e2169972d23075554ae14d73722c53c", + "https://deno.land/std@0.204.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16", + "https://deno.land/std@0.204.0/path/windows/mod.ts": "c3d1a36fbf9f6db1320bcb4fbda8de011d25461be3497105e15cbea1e3726198", + "https://deno.land/std@0.204.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69", + "https://deno.land/std@0.204.0/path/windows/parse.ts": "120faf778fe1f22056f33ded069b68e12447668fcfa19540c0129561428d3ae5", + "https://deno.land/std@0.204.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649", + "https://deno.land/std@0.204.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b", + "https://deno.land/std@0.204.0/path/windows/separator.ts": "ae21f27015f10510ed1ac4a0ba9c4c9c967cbdd9d9e776a3e4967553c397bd5d", + "https://deno.land/std@0.204.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3", + "https://deno.land/std@0.204.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d", + "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", + "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", + "https://deno.land/std@0.224.0/fs/_create_walk_entry.ts": "5d9d2aaec05bcf09a06748b1684224d33eba7a4de24cf4cf5599991ca6b5b412", + "https://deno.land/std@0.224.0/fs/_get_file_info_type.ts": "da7bec18a7661dba360a1db475b826b18977582ce6fc9b25f3d4ee0403fe8cbd", + "https://deno.land/std@0.224.0/fs/_is_same_path.ts": "709c95868345fea051c58b9e96af95cff94e6ae98dfcff2b66dee0c212c4221f", + "https://deno.land/std@0.224.0/fs/_is_subdir.ts": "c68b309d46cc8568ed83c000f608a61bbdba0943b7524e7a30f9e450cf67eecd", + "https://deno.land/std@0.224.0/fs/_to_path_string.ts": "29bfc9c6c112254961d75cbf6ba814d6de5349767818eb93090cecfa9665591e", + "https://deno.land/std@0.224.0/fs/copy.ts": "7ab12a16adb65d155d4943c88081ca16ce3b0b5acada64c1ce93800653678039", + "https://deno.land/std@0.224.0/fs/empty_dir.ts": "e400e96e1d2c8c558a5a1712063bd43939e00619c1d1cc29959babc6f1639418", + "https://deno.land/std@0.224.0/fs/ensure_dir.ts": "51a6279016c65d2985f8803c848e2888e206d1b510686a509fa7cc34ce59d29f", + "https://deno.land/std@0.224.0/fs/ensure_file.ts": "67608cf550529f3d4aa1f8b6b36bf817bdc40b14487bf8f60e61cbf68f507cf3", + "https://deno.land/std@0.224.0/fs/ensure_link.ts": "5c98503ebfa9cc05e2f2efaa30e91e60b4dd5b43ebbda82f435c0a5c6e3ffa01", + "https://deno.land/std@0.224.0/fs/ensure_symlink.ts": "cafe904cebacb9a761977d6dbf5e3af938be946a723bb394080b9a52714fafe4", + "https://deno.land/std@0.224.0/fs/eol.ts": "18c4ac009d0318504c285879eb7f47942643f13619e0ff070a0edc59353306bd", + "https://deno.land/std@0.224.0/fs/exists.ts": "3d38cb7dcbca3cf313be343a7b8af18a87bddb4b5ca1bd2314be12d06533b50f", + "https://deno.land/std@0.224.0/fs/expand_glob.ts": "2e428d90acc6676b2aa7b5c78ef48f30641b13f1fe658e7976c9064fb4b05309", + "https://deno.land/std@0.224.0/fs/mod.ts": "c25e6802cbf27f3050f60b26b00c2d8dba1cb7fcdafe34c66006a7473b7b34d4", + "https://deno.land/std@0.224.0/fs/move.ts": "ca205d848908d7f217353bc5c623627b1333490b8b5d3ef4cab600a700c9bd8f", + "https://deno.land/std@0.224.0/fs/walk.ts": "cddf87d2705c0163bff5d7767291f05b0f46ba10b8b28f227c3849cace08d303", + "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8", + "https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2", + "https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c", + "https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.224.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf", + "https://deno.land/std@0.224.0/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d", + "https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3", + "https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a", + "https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15", + "https://deno.land/std@0.224.0/path/basename.ts": "7ee495c2d1ee516ffff48fb9a93267ba928b5a3486b550be73071bc14f8cc63e", + "https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36", + "https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c", + "https://deno.land/std@0.224.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069", + "https://deno.land/std@0.224.0/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972", + "https://deno.land/std@0.224.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7", + "https://deno.land/std@0.224.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141", + "https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a", + "https://deno.land/std@0.224.0/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0", + "https://deno.land/std@0.224.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352", + "https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d", + "https://deno.land/std@0.224.0/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0", + "https://deno.land/std@0.224.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1", + "https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00", + "https://deno.land/std@0.224.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40", + "https://deno.land/std@0.224.0/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f", + "https://deno.land/std@0.224.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede", + "https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63", + "https://deno.land/std@0.224.0/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", + "https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91", + "https://deno.land/std@0.224.0/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", + "https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf", + "https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d", + "https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808", + "https://deno.land/std@0.224.0/path/windows/basename.ts": "6bbc57bac9df2cec43288c8c5334919418d784243a00bc10de67d392ab36d660", + "https://deno.land/std@0.224.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5", + "https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9", + "https://deno.land/std@0.224.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01", + "https://deno.land/std@0.224.0/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8", + "https://deno.land/std@0.224.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a", + "https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf", + "https://deno.land/std@0.224.0/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", + "https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780", + "https://deno.land/std@0.224.0/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", + "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972" + }, + "workspace": { + "dependencies": [ + "jsr:@openai/openai@^5.8.2", + "jsr:@std/assert@1" + ], + "packageJson": { + "dependencies": [ + "npm:jimp@^1.6.0" + ] + } + } +} diff --git a/imjen.example.config.jsonc b/imjen.example.config.jsonc new file mode 100644 index 0000000..33403a7 --- /dev/null +++ b/imjen.example.config.jsonc @@ -0,0 +1,32 @@ +{ + // Optional: Set API key in config instead of environment + // "OPENAI_API_KEY": "your_key_here", + + // Default dimensions (optional) + "width": 1024, + "height": 1024, + + // Default prompt template (optional) + "promptTemplate": "ui-icon", + + // Generate multiple images + "images": [ + { + "description": "Compass Rose for the central navigation display", + "promptArg": "minimal flat compass rose icon", + "width": 512, + "height": 512 + }, + { + "description": "Wind Direction Arrow", + "promptTemplate": "flat-icon", + "width": 256, + "height": 256 + }, + { + "description": "Settings Gear Icon", + "promptTemplate": "outline-icon" + } + // Add more objects as needed + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..de76143 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "imjen", + "version": "0.0.8", + "author": "imjen Contributors", + "bin": { + "imjen": "./dist/imjen" + }, + "scripts": { + "build": "rm -rf dist && deno task build", + "prepublishOnly": "npm run build" + }, + "files": ["dist"], + "dependencies": { + "jimp": "^1.6.0" + } +} \ No newline at end of file diff --git a/src/default_config.ts b/src/default_config.ts new file mode 100644 index 0000000..2618b46 --- /dev/null +++ b/src/default_config.ts @@ -0,0 +1,43 @@ +export default ` +{ +// "REPLICATE_API_TOKEN": "your_token_here", + "imageModel": "stability-ai/sdxl", + "displayTarget": "browser", + "width": 512, + "height": 512, + "promptTemplate": "ui-icon", + "images": [ + { + "description": "Compass Rose for the central navigation display", + "promptTemplate": "ui-icon" + }, + { + "description": "Wind Direction Arrow", + "promptTemplate": "flat-icon", + "width": 256, + "height": 256 + }, + { + "description": "Settings gear icon", + "promptTemplate": "outline-icon" + }, + { + "description": "Beautiful sunset landscape", + "promptTemplate": "realistic", + "width": 1024, + "height": 768 + }, + { + "description": "Abstract geometric pattern", + "promptTemplate": "artistic" + }, + { + "description": "Custom prompt example", + "promptArg": "futuristic neon-lit cyberpunk cityscape, high contrast, vibrant colors" + } + // Available templates: ui-icon, flat-icon, outline-icon, realistic, artistic, custom + // Use "promptArg" for complete custom prompts (overrides templates) + // Use "promptTemplate" + "description" for template-based generation + ] +} +` diff --git a/src/imjen.ts b/src/imjen.ts new file mode 100644 index 0000000..b94f068 --- /dev/null +++ b/src/imjen.ts @@ -0,0 +1,152 @@ +import OpenAI from "jsr:@openai/openai"; +import {Jimp} from "jimp"; + + +interface ImageConfig { + description: string; + promptTemplate?: string; + promptArg?: string; + width?: number; + height?: number; + displayTarget?: string; +} + +interface Config { + imageModel?: string; + displayTarget?: string; + width?: number; + height?: number; + promptTemplate?: string; + images: ImageConfig[]; +} + +export class ImjenApp { + private promptTemplates: Record = { + "ui-icon": "minimalist UI icon, clean design, simple shapes, flat style, {description}", + "flat-icon": "flat icon design, simple geometric shapes, solid colors, {description}", + "outline-icon": "outline icon, line art style, minimal design, {description}", + "realistic": "photorealistic image, high quality, detailed, {description}", + "artistic": "artistic interpretation, creative style, expressive, {description}", + }; + + async run() { + console.log("šŸš€ Starting image generation..."); + + try { + const config = await this.loadConfig(); + console.log(`šŸ“‹ Loaded configuration with ${config.images.length} images to generate`); + + const openai = new OpenAI({ + apiKey: Deno.env.get("OPENAI_API_KEY"), + }); + + for (let i = 0; i < config.images.length; i++) { + const imageConfig = config.images[i]; + console.log(`\nšŸŽØ Generating image ${i + 1}/${config.images.length}: ${imageConfig.description}`); + + try { + await this.generateImage({ openai, config, imageConfig, index: i + 1 }); + } catch (error) { + console.error(`āŒ Failed to generate image ${i + 1}: ${error.message}`); + continue; // Continue with next image + } + } + + console.log("\nāœ… Image generation completed!"); + } catch (error) { + console.error("āŒ Error during image generation:", error.message); + throw error; + } + } + + private async loadConfig(): Promise { + try { + const configText = await Deno.readTextFile("imjen.config.jsonc"); + // Simple JSONC parser - remove comments and parse + const jsonText = configText + .split('\n') + .map(line => line.replace(/\/\/.*$/, '').trim()) + .filter(line => line.length > 0) + .join('\n'); + + return JSON.parse(jsonText); + } catch (error) { + throw new Error(`Failed to load config file: ${error.message}`); + } + } + + private buildPrompt(config: Config, imageConfig: ImageConfig): string { + // Use custom prompt if provided + if (imageConfig.promptArg) { + return imageConfig.promptArg; + } + + // Use template-based prompt + const template = imageConfig.promptTemplate || config.promptTemplate || "realistic"; + const templateText = this.promptTemplates[template]; + + if (!templateText) { + throw new Error(`Unknown prompt template: ${template}`); + } + + return templateText.replace("{description}", imageConfig.description); + } + + private async generateImage({ openai, config, imageConfig, index }: { + openai: OpenAI; + config: Config; + imageConfig: ImageConfig; + index: number; + }) { + const prompt = this.buildPrompt(config, imageConfig); + const width = imageConfig.width || config.width || 1024; + const height = imageConfig.height || config.height || 1024; + const size = `${width}x${height}` as "1024x1024" | "1792x1024" | "1024x1792"; + + console.log(` šŸ“ Prompt: ${prompt}`); + console.log(` šŸ“ Size: ${size}`); + + const result = await openai.images.generate({ + model: "dall-e-3", + prompt, + size: "1024x1024", + }); + + + + + console.log(` šŸ”— Generated URL: ${result.data[0].url}`); + + // Create filename from description + const safeDescription = imageConfig.description + .toLowerCase() + .replace(/[^a-z0-9\s]/g, '') + .replace(/\s+/g, '_') + .substring(0, 50); + const filename = `${String(index).padStart(2, '0')}_${safeDescription}.png`; + + try { + const imageResponse = await fetch(result.data[0].url!); + if (!imageResponse.ok) { + throw new Error(`Failed to fetch image: ${imageResponse.statusText}`); + } + + const imageData = await imageResponse.arrayBuffer(); + await Deno.writeFile(filename, new Uint8Array(imageData)); + + // open a file called "lenna.png" + const image = await Jimp.read(filename); + + image.resize({ + w: width, + h: height + }); + + await image.write(filename as any); + console.log(` šŸ’¾ Image saved as: ${filename}`); + } catch (error) { + console.error(` āŒ Error saving image: ${error.message}`); + throw error; + } + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100755 index 0000000..5364abf --- /dev/null +++ b/src/main.ts @@ -0,0 +1,13 @@ +import {ImjenApp} from "./imjen.ts"; +import {existsSync} from "https://deno.land/std/fs/mod.ts"; +import default_config from "./default_config.ts"; + +if (import.meta.main) { + const app = new ImjenApp(); + + if (!existsSync("imjen.config.jsonc")) { + await Deno.writeTextFile("imjen.config.jsonc", default_config); + } + + await app.run(); +} \ No newline at end of file diff --git a/src/main_test.ts b/src/main_test.ts new file mode 100644 index 0000000..e69de29