undraw-cli 0.1.1 β†’ 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,53 +1,89 @@
1
- # unDraw CLI
1
+ # unDraw CLI 🎭
2
2
 
3
- A powerful CLI to search, customize, and download illustrations from [unDraw](https://undraw.co).
3
+ [![npm version](https://img.shields.io/npm/v/undraw-cli.svg)](https://www.npmjs.com/package/undraw-cli)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/stefdevscore/undraw-cli/blob/main/CONTRIBUTING.md)
4
6
 
5
- ## Features
7
+ > A high-performance, minimalist CLI to search, customize, and download the entire [unDraw](https://undraw.co) library (1,650+ illustrations) for your next project.
6
8
 
7
- - πŸ” **Search**: Find illustrations by keyword.
8
- - 🎨 **Customize**: Change the primary color (`#6c63ff`) to any hex code on-the-fly.
9
- - πŸ“¦ **Download**: Save customized SVGs directly to your project.
9
+ ---
10
10
 
11
- ## Installation
11
+ ## ✨ Features
12
12
 
13
- ```bash
14
- npm install -g undraw-cli
15
- ```
13
+ - **πŸš€ Instant Search**: Search 1,650+ illustrations by keyword from a local, optimized inventory.
14
+ - **🎨 On-the-Fly Customization**: Automatically replace the default unDraw color with your brand's hex code.
15
+ - **πŸ“¦ Tiny Footprint**: Only **66 kB** unpacked. No bloat, just performance.
16
+ - **πŸ€– Agentic Ready**: Built for AI developers who need structured access to high-quality SVG assets.
16
17
 
17
- _(Note: Use `npx undraw-cli` if you don't want to install globally)_
18
+ ---
18
19
 
19
- ## Usage
20
+ ## 🎨 Zero-Config Customization
20
21
 
21
- ### Sync the full library
22
+ One command to match your brand. No browser required.
22
23
 
23
- Run this first to build your local inventory (1,600+ items).
24
+ | Original (`#6c63ff`) | Customized (`#ff0077`) |
25
+ | :--- | :--- |
26
+ | ![Original](./docs/assets/astronomy_original.svg) | ![Customized](./docs/assets/astronomy_pink.svg) |
24
27
 
25
28
  ```bash
26
- undraw sync
29
+ undraw download astronomy_ied1 --color #ff0077
27
30
  ```
28
31
 
29
- ### List illustrations
32
+ ---
30
33
 
31
- ```bash
32
- undraw list --page 1 --limit 20
33
- ```
34
+ ## πŸš€ Quick Start
34
35
 
35
- ### Search for illustrations
36
+ ### Installation
36
37
 
37
38
  ```bash
38
- undraw search astronomy
39
+ npm install -g undraw-cli
39
40
  ```
40
41
 
41
- ### Download and Customize
42
+ ### Usage
42
43
 
43
- ```bash
44
- undraw download astronomy_ied1 --color #32a852 --output ./assets
45
- ```
44
+ 1. **Sync the library** (updates your local inventory with the latest illustrations):
45
+ ```bash
46
+ undraw sync
47
+ ```
48
+
49
+ 2. **Search for an illustration**:
50
+ ```bash
51
+ undraw search "astronomy"
52
+ ```
53
+
54
+ 3. **Download with a custom color**:
55
+ ```bash
56
+ undraw download astronomy_ied1 --color #34d399
57
+ ```
58
+
59
+ ---
60
+
61
+ ## πŸ› οΈ Commands
62
+
63
+ - `undraw sync`: Crawls unDraw.co and builds a local metadata cache.
64
+ - `undraw list`: Browse the library with pagination (83+ pages).
65
+ - `undraw search <query>`: Search the inventory by title.
66
+ - `undraw download <id>`: Fetch the SVG and apply a custom hex color.
67
+
68
+ ---
69
+
70
+ ## πŸ—ΊοΈ Roadmap
71
+ Check out our [Roadmap](./docs/roadmap.md) for planned features like Interactive TUI, Full Mirroring, and ANSI Previews.
72
+
73
+ ---
74
+
75
+ ## πŸ™ Credits & Attribution
46
76
 
47
- ## How it works
77
+ ### unDraw Illustrations
78
+ The illustrations are provided by the amazing **Katerina Limpitsouni** at [unDraw.co](https://undraw.co).
79
+ If you love these illustrations, please visit their website and support their work!
48
80
 
49
- The CLI fetches the latest illustration metadata from unDraw and performs a high-performance string replacement on the SVG source code to apply your custom primary color.
81
+ ### Tooling
82
+ - **Commander.js** for the CLI engine.
83
+ - **tsup** for high-speed ESM bundling.
84
+ - **Chalk** & **Ora** for the terminal experience.
50
85
 
51
- ## License
86
+ ---
52
87
 
53
- MIT
88
+ ## βš–οΈ License
89
+ MIT Β© [azk](https://github.com/stefdevscore)
package/dist/index.js CHANGED
@@ -1,127 +1,4 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/index.ts
4
- import { Command } from "commander";
5
- import fetch from "node-fetch";
6
- import fs from "fs/promises";
7
- import path from "path";
8
- import chalk from "chalk";
9
- import ora from "ora";
10
- var program = new Command();
11
- var DEFAULT_COLOR = "#6c63ff";
12
- var BASE_URL = "https://undraw.co";
13
- var CDN_URL = "https://cdn.undraw.co/illustration";
14
- var INVENTORY_PATH = path.resolve(process.cwd(), "undraw-inventory.json");
15
- async function getBuildId() {
16
- const response = await fetch(BASE_URL);
17
- const html = await response.text();
18
- const match = html.match(/"buildId":"([^"]+)"/);
19
- return match ? match[1] : null;
20
- }
21
- async function fetchIllustrationsPage(buildId, page) {
22
- const url = page === 1 ? `${BASE_URL}/_next/data/${buildId}/illustrations.json` : `${BASE_URL}/_next/data/${buildId}/illustrations/${page}.json?page=${page}`;
23
- const response = await fetch(url);
24
- if (!response.ok) return null;
25
- const data = await response.json();
26
- return data.pageProps.illustrations;
27
- }
28
- async function loadInventory() {
29
- try {
30
- const data = await fs.readFile(INVENTORY_PATH, "utf-8");
31
- return JSON.parse(data);
32
- } catch (_err) {
33
- return null;
34
- }
35
- }
36
- function printIllustrations(illustrations, title) {
37
- if (illustrations.length === 0) {
38
- console.log(chalk.red("No illustrations found."));
39
- return;
40
- }
41
- console.log(chalk.green(`
42
- ${title} (${illustrations.length}):`));
43
- console.log(chalk.gray("\u2500".repeat(50)));
44
- illustrations.forEach((i) => {
45
- console.log(
46
- `${chalk.bold((i.title || "Untitled").padEnd(30))} id: ${chalk.cyan(i.newSlug || i.slug)}`
47
- );
48
- });
49
- console.log(chalk.gray("\u2500".repeat(50)) + "\n");
50
- }
51
- program.name("undraw").description("CLI to search and customize unDraw illustrations").version("0.1.0");
52
- program.command("sync").description("Sync the full unDraw library to a local inventory file").action(async () => {
53
- const spinner = ora("Initializing sync...").start();
54
- try {
55
- const buildId = await getBuildId();
56
- if (!buildId) throw new Error("Could not find buildId");
57
- let allIllustrations = [];
58
- let page = 1;
59
- let hasMore = true;
60
- while (hasMore) {
61
- spinner.text = `Fetching page ${page}... (Total: ${allIllustrations.length})`;
62
- const illustrations = await fetchIllustrationsPage(buildId, page);
63
- if (!illustrations || illustrations.length === 0) {
64
- hasMore = false;
65
- } else {
66
- allIllustrations = [...allIllustrations, ...illustrations];
67
- page++;
68
- }
69
- }
70
- spinner.text = "Saving inventory...";
71
- await fs.writeFile(INVENTORY_PATH, JSON.stringify(allIllustrations, null, 2));
72
- spinner.succeed(
73
- chalk.green(`Synced ${allIllustrations.length} illustrations to ${INVENTORY_PATH}`)
74
- );
75
- } catch (_err) {
76
- spinner.fail(chalk.red(`Sync failed: ${_err.message}`));
77
- }
78
- });
79
- program.command("search").description("Search for illustrations by title").argument("<query>", "search query").action(async (query) => {
80
- const illustrations = await loadInventory();
81
- if (!illustrations) {
82
- console.log(chalk.yellow('Inventory not found. Please run "undraw sync" first.'));
83
- return;
84
- }
85
- const filtered = illustrations.filter(
86
- (i) => (i.title || "").toLowerCase().includes(query.toLowerCase())
87
- );
88
- printIllustrations(filtered, `Found ${filtered.length} matches for "${query}"`);
89
- });
90
- program.command("list").description("List illustrations from local inventory").option("-p, --page <number>", "page number", "1").option("-l, --limit <number>", "items per page", "20").action(async (options) => {
91
- const illustrations = await loadInventory();
92
- if (!illustrations) {
93
- console.log(chalk.yellow('Inventory not found. Please run "undraw sync" first.'));
94
- return;
95
- }
96
- const page = parseInt(options.page, 10);
97
- const limit = parseInt(options.limit, 10);
98
- const start = (page - 1) * limit;
99
- const end = start + limit;
100
- const paginated = illustrations.slice(start, end);
101
- const totalPages = Math.ceil(illustrations.length / limit);
102
- printIllustrations(paginated, `Inventory: Page ${page} of ${totalPages}`);
103
- if (page < totalPages) {
104
- console.log(chalk.gray(`Run 'undraw list --page ${page + 1}' for more...`));
105
- }
106
- });
107
- program.command("download").description("Download an illustration with a custom color").argument("<id>", "illustration id (e.g., astronomy_ied1)").option("-c, --color <hex>", "primary hex color", DEFAULT_COLOR).option("-o, --output <path>", "output file path", ".").action(async (id, options) => {
108
- const spinner = ora(`Downloading ${id}...`).start();
109
- try {
110
- const url = `${CDN_URL}/${id}.svg`;
111
- const response = await fetch(url);
112
- if (!response.ok) throw new Error(`Illustration not found: ${id}`);
113
- let svg = await response.text();
114
- if (options.color !== DEFAULT_COLOR) {
115
- spinner.text = `Applying color ${options.color}...`;
116
- svg = svg.split(DEFAULT_COLOR).join(options.color);
117
- }
118
- const filename = `${id}.svg`;
119
- const outputPath = path.resolve(options.output, filename);
120
- await fs.writeFile(outputPath, svg);
121
- spinner.succeed(chalk.green(`Saved to ${outputPath}`));
122
- } catch (err) {
123
- spinner.fail(chalk.red(`Error: ${err.message}`));
124
- }
125
- });
126
- program.parse();
127
- //# sourceMappingURL=index.js.map
2
+ import{Command as $}from"commander";import p from"node-fetch";import f from"fs/promises";import m from"path";import r from"chalk";import y from"ora";var l=new $,u="#6c63ff",d="https://undraw.co",v="https://cdn.undraw.co/illustration",g=m.resolve(process.cwd(),"undraw-inventory.json");async function I(){let n=(await(await p(d)).text()).match(/"buildId":"([^"]+)"/);return n?n[1]:null}async function b(t,o){let n=o===1?`${d}/_next/data/${t}/illustrations.json`:`${d}/_next/data/${t}/illustrations/${o}.json?page=${o}`,e=await p(n);return e.ok?(await e.json()).pageProps.illustrations:null}async function h(){try{let t=await f.readFile(g,"utf-8");return JSON.parse(t)}catch{return null}}function w(t,o){if(t.length===0){console.log(r.red("No illustrations found."));return}console.log(r.green(`
3
+ ${o} (${t.length}):`)),console.log(r.gray("\u2500".repeat(50))),t.forEach(n=>{console.log(`${r.bold((n[1]||"Untitled").padEnd(30))} id: ${r.cyan(n[0])}`)}),console.log(r.gray("\u2500".repeat(50))+`
4
+ `)}l.name("undraw").description("CLI to search and customize unDraw illustrations").version("0.1.1");l.command("sync").description("Sync the full unDraw library to a local inventory file").action(async()=>{let t=y("Initializing sync...").start();try{let o=await I();if(!o)throw new Error("Could not find buildId");let n=[],e=1,a=!0;for(;a;){t.text=`Fetching page ${e}... (Items: ${n.length})`;let s=await b(o,e);!s||s.length===0?a=!1:(s.forEach(i=>{n.push([i.newSlug,i.title])}),e++)}t.text="Saving inventory...",await f.writeFile(g,JSON.stringify(n)),t.succeed(r.green(`Synced ${n.length} illustrations to ${g}`))}catch(o){t.fail(r.red(`Sync failed: ${o.message}`))}});l.command("search").description("Search for illustrations by title").argument("<query>","search query").action(async t=>{let o=await h();if(!o){console.log(r.yellow('Inventory not found. Please run "undraw sync" first.'));return}let n=o.filter(e=>(e[1]||"").toLowerCase().includes(t.toLowerCase()));w(n,`Found ${n.length} matches for "${t}"`)});l.command("list").description("List illustrations from local inventory").option("-p, --page <number>","page number","1").option("-l, --limit <number>","items per page","20").action(async t=>{let o=await h();if(!o){console.log(r.yellow('Inventory not found. Please run "undraw sync" first.'));return}let n=parseInt(t.page,10),e=parseInt(t.limit,10),a=(n-1)*e,s=a+e,i=o.slice(a,s),c=Math.ceil(o.length/e);w(i,`Inventory: Page ${n} of ${c}`),n<c&&console.log(r.gray(`Run 'undraw list --page ${n+1}' for more...`))});l.command("download").description("Download an illustration with a custom color").argument("<id>","illustration id (e.g., astronomy_ied1)").option("-c, --color <hex>","primary hex color",u).option("-o, --output <path>","output file path",".").action(async(t,o)=>{let n=y(`Downloading ${t}...`).start();try{let e=`${v}/${t}.svg`,a=await p(e);if(!a.ok)throw new Error(`Illustration not found: ${t}`);let s=await a.text();o.color!==u&&(n.text=`Applying color ${o.color}...`,s=s.split(u).join(o.color));let i=`${t}.svg`,c=m.resolve(o.output,i);await f.writeFile(c,s),n.succeed(r.green(`Saved to ${c}`))}catch(e){n.fail(r.red(`Error: ${e.message}`))}});l.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "undraw-cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A CLI to search, customize, and download illustrations from undraw.co",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",