@nice2dev/icons 1.0.0 → 1.0.3
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/CHANGELOG.md +169 -7
- package/README.md +247 -2
- package/bin/ntd-icons.mjs +732 -0
- package/dist/cjs/NtdIconBadge.js +375 -0
- package/dist/cjs/NtdIconBadge.js.map +1 -0
- package/dist/cjs/NtdIconMorph.js +386 -0
- package/dist/cjs/NtdIconMorph.js.map +1 -0
- package/dist/cjs/NtdStateMorph.js +177 -0
- package/dist/cjs/NtdStateMorph.js.map +1 -0
- package/dist/cjs/createIcon.js +51 -4
- package/dist/cjs/createIcon.js.map +1 -1
- package/dist/cjs/figmaSync.js +308 -0
- package/dist/cjs/figmaSync.js.map +1 -0
- package/dist/cjs/iconAnalytics.js +189 -0
- package/dist/cjs/iconAnalytics.js.map +1 -0
- package/dist/cjs/iconMetadata.js +523 -0
- package/dist/cjs/iconMetadata.js.map +1 -0
- package/dist/cjs/iconStyles.js +597 -0
- package/dist/cjs/iconStyles.js.map +1 -0
- package/dist/cjs/icons/accessibility.js +168 -0
- package/dist/cjs/icons/accessibility.js.map +1 -0
- package/dist/cjs/icons/ai.js +191 -0
- package/dist/cjs/icons/ai.js.map +1 -0
- package/dist/cjs/icons/brand.js +70 -0
- package/dist/cjs/icons/brand.js.map +1 -1
- package/dist/cjs/icons/education.js +212 -0
- package/dist/cjs/icons/education.js.map +1 -0
- package/dist/cjs/icons/food.js +198 -0
- package/dist/cjs/icons/food.js.map +1 -0
- package/dist/cjs/icons/gaming.js +191 -0
- package/dist/cjs/icons/gaming.js.map +1 -0
- package/dist/cjs/icons/index.js +520 -0
- package/dist/cjs/icons/index.js.map +1 -0
- package/dist/cjs/icons/legal.js +168 -0
- package/dist/cjs/icons/legal.js.map +1 -0
- package/dist/cjs/icons/realestate.js +212 -0
- package/dist/cjs/icons/realestate.js.map +1 -0
- package/dist/cjs/icons/science.js +201 -0
- package/dist/cjs/icons/science.js.map +1 -0
- package/dist/cjs/icons/sports.js +176 -0
- package/dist/cjs/icons/sports.js.map +1 -0
- package/dist/cjs/icons/sustainability.js +193 -0
- package/dist/cjs/icons/sustainability.js.map +1 -0
- package/dist/cjs/icons/travel.js +184 -0
- package/dist/cjs/icons/travel.js.map +1 -0
- package/dist/cjs/index.js +265 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lottieIntegration.js +286 -0
- package/dist/cjs/lottieIntegration.js.map +1 -0
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/cjs/particleEffects.js +259 -0
- package/dist/cjs/particleEffects.js.map +1 -0
- package/dist/cjs/resolver.js +224 -0
- package/dist/cjs/resolver.js.map +1 -0
- package/dist/cjs/tailwind-plugin.js +340 -0
- package/dist/cjs/tailwind-plugin.js.map +1 -0
- package/dist/cjs/types.js +38 -0
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/variantProps.js +78 -0
- package/dist/cjs/variantProps.js.map +1 -0
- package/dist/esm/NtdIconBadge.js +370 -0
- package/dist/esm/NtdIconBadge.js.map +1 -0
- package/dist/esm/NtdIconMorph.js +381 -0
- package/dist/esm/NtdIconMorph.js.map +1 -0
- package/dist/esm/NtdStateMorph.js +174 -0
- package/dist/esm/NtdStateMorph.js.map +1 -0
- package/dist/esm/createIcon.js +53 -6
- package/dist/esm/createIcon.js.map +1 -1
- package/dist/esm/figmaSync.js +304 -0
- package/dist/esm/figmaSync.js.map +1 -0
- package/dist/esm/iconAnalytics.js +185 -0
- package/dist/esm/iconAnalytics.js.map +1 -0
- package/dist/esm/iconMetadata.js +510 -0
- package/dist/esm/iconMetadata.js.map +1 -0
- package/dist/esm/iconStyles.js +585 -0
- package/dist/esm/iconStyles.js.map +1 -0
- package/dist/esm/icons/accessibility.js +153 -0
- package/dist/esm/icons/accessibility.js.map +1 -0
- package/dist/esm/icons/ai.js +174 -0
- package/dist/esm/icons/ai.js.map +1 -0
- package/dist/esm/icons/brand.js +67 -1
- package/dist/esm/icons/brand.js.map +1 -1
- package/dist/esm/icons/education.js +193 -0
- package/dist/esm/icons/education.js.map +1 -0
- package/dist/esm/icons/food.js +180 -0
- package/dist/esm/icons/food.js.map +1 -0
- package/dist/esm/icons/gaming.js +174 -0
- package/dist/esm/icons/gaming.js.map +1 -0
- package/dist/esm/icons/index.js +31 -0
- package/dist/esm/icons/index.js.map +1 -0
- package/dist/esm/icons/legal.js +153 -0
- package/dist/esm/icons/legal.js.map +1 -0
- package/dist/esm/icons/realestate.js +193 -0
- package/dist/esm/icons/realestate.js.map +1 -0
- package/dist/esm/icons/science.js +183 -0
- package/dist/esm/icons/science.js.map +1 -0
- package/dist/esm/icons/sports.js +160 -0
- package/dist/esm/icons/sports.js.map +1 -0
- package/dist/esm/icons/sustainability.js +176 -0
- package/dist/esm/icons/sustainability.js.map +1 -0
- package/dist/esm/icons/travel.js +167 -0
- package/dist/esm/icons/travel.js.map +1 -0
- package/dist/esm/index.js +27 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lottieIntegration.js +282 -0
- package/dist/esm/lottieIntegration.js.map +1 -0
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/esm/particleEffects.js +257 -0
- package/dist/esm/particleEffects.js.map +1 -0
- package/dist/esm/resolver.js +221 -0
- package/dist/esm/resolver.js.map +1 -0
- package/dist/esm/tailwind-plugin.js +337 -0
- package/dist/esm/tailwind-plugin.js.map +1 -0
- package/dist/esm/types.js +37 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/variantProps.js +68 -0
- package/dist/esm/variantProps.js.map +1 -0
- package/dist/types/NtdIconBadge.d.ts +209 -0
- package/dist/types/NtdIconMorph.d.ts +183 -0
- package/dist/types/NtdStateMorph.d.ts +100 -0
- package/dist/types/client.d.ts +27 -0
- package/dist/types/design-tokens.d.ts +281 -0
- package/dist/types/figmaSync.d.ts +135 -0
- package/dist/types/headless-ui.d.ts +462 -0
- package/dist/types/iconAnalytics.d.ts +131 -0
- package/dist/types/iconMetadata.d.ts +123 -0
- package/dist/types/iconStyles.d.ts +104 -0
- package/dist/types/icons/accessibility.d.ts +39 -0
- package/dist/types/icons/ai.d.ts +43 -0
- package/dist/types/icons/brand.d.ts +4 -0
- package/dist/types/icons/education.d.ts +47 -0
- package/dist/types/icons/food.d.ts +45 -0
- package/dist/types/icons/gaming.d.ts +42 -0
- package/dist/types/icons/index.d.ts +12 -1
- package/dist/types/icons/legal.d.ts +39 -0
- package/dist/types/icons/realestate.d.ts +47 -0
- package/dist/types/icons/science.d.ts +45 -0
- package/dist/types/icons/sports.d.ts +41 -0
- package/dist/types/icons/sustainability.d.ts +43 -0
- package/dist/types/icons/travel.d.ts +43 -0
- package/dist/types/index.d.ts +28 -6
- package/dist/types/lottieIntegration.d.ts +173 -0
- package/dist/types/micro-interactions.d.ts +146 -0
- package/dist/types/microInteractions.d.ts +174 -0
- package/dist/types/particleEffects.d.ts +69 -0
- package/dist/types/resolver.d.ts +58 -0
- package/dist/types/rsc.d.ts +159 -0
- package/dist/types/tailwind-plugin.d.ts +100 -0
- package/dist/types/types.d.ts +100 -0
- package/dist/types/utilities.d.ts +221 -0
- package/dist/types/variantProps.d.ts +122 -0
- package/package.json +58 -5
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @file ntd-icons CLI
|
|
4
|
+
* @description Command-line interface for @nice2dev/icons library
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* preview <icon> Open icon preview in browser
|
|
8
|
+
* list List all icons
|
|
9
|
+
* export Export icons to various formats
|
|
10
|
+
* search Search icons by name or tags
|
|
11
|
+
*
|
|
12
|
+
* @version 1.0.0
|
|
13
|
+
* @license MIT
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { spawn } from 'child_process';
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
import http from 'http';
|
|
21
|
+
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = path.dirname(__filename);
|
|
24
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
25
|
+
|
|
26
|
+
// ANSI colors for terminal output
|
|
27
|
+
const colors = {
|
|
28
|
+
reset: '\x1b[0m',
|
|
29
|
+
bright: '\x1b[1m',
|
|
30
|
+
dim: '\x1b[2m',
|
|
31
|
+
red: '\x1b[31m',
|
|
32
|
+
green: '\x1b[32m',
|
|
33
|
+
yellow: '\x1b[33m',
|
|
34
|
+
blue: '\x1b[34m',
|
|
35
|
+
magenta: '\x1b[35m',
|
|
36
|
+
cyan: '\x1b[36m',
|
|
37
|
+
white: '\x1b[37m',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const log = {
|
|
41
|
+
info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
|
|
42
|
+
success: (msg) => console.log(`${colors.green}✔${colors.reset} ${msg}`),
|
|
43
|
+
warn: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
|
|
44
|
+
error: (msg) => console.error(`${colors.red}✖${colors.reset} ${msg}`),
|
|
45
|
+
title: (msg) => console.log(`\n${colors.bright}${colors.cyan}${msg}${colors.reset}\n`),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Parse command line arguments
|
|
49
|
+
function parseArgs(args) {
|
|
50
|
+
const result = {
|
|
51
|
+
command: null,
|
|
52
|
+
positional: [],
|
|
53
|
+
flags: {},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
for (let i = 0; i < args.length; i++) {
|
|
57
|
+
const arg = args[i];
|
|
58
|
+
|
|
59
|
+
if (arg.startsWith('--')) {
|
|
60
|
+
const [key, value] = arg.slice(2).split('=');
|
|
61
|
+
result.flags[key] = value ?? true;
|
|
62
|
+
} else if (arg.startsWith('-') && arg.length === 2) {
|
|
63
|
+
result.flags[arg.slice(1)] = args[++i] ?? true;
|
|
64
|
+
} else if (!result.command) {
|
|
65
|
+
result.command = arg;
|
|
66
|
+
} else {
|
|
67
|
+
result.positional.push(arg);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get list of all icons from source files
|
|
75
|
+
async function getIconList() {
|
|
76
|
+
const iconsDir = path.join(packageRoot, 'src', 'icons');
|
|
77
|
+
const icons = [];
|
|
78
|
+
const categories = {};
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const files = fs.readdirSync(iconsDir).filter(f => f.endsWith('.tsx') && f !== 'index.ts');
|
|
82
|
+
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
const category = file.replace('.tsx', '');
|
|
85
|
+
const content = fs.readFileSync(path.join(iconsDir, file), 'utf-8');
|
|
86
|
+
|
|
87
|
+
// Match all exported const NtdXxx = createIcon patterns
|
|
88
|
+
const iconMatches = content.matchAll(/export const (Ntd\w+)\s*=\s*createIcon/g);
|
|
89
|
+
const categoryIcons = [];
|
|
90
|
+
|
|
91
|
+
for (const match of iconMatches) {
|
|
92
|
+
const iconName = match[1];
|
|
93
|
+
icons.push({ name: iconName, category, file });
|
|
94
|
+
categoryIcons.push(iconName);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (categoryIcons.length > 0) {
|
|
98
|
+
categories[category] = categoryIcons;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (err) {
|
|
102
|
+
log.error(`Failed to read icons: ${err.message}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { icons, categories };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Generate HTML preview page for icons
|
|
109
|
+
function generatePreviewHtml(iconNames, title = 'Icon Preview') {
|
|
110
|
+
const icons = iconNames.map(name => `
|
|
111
|
+
<div class="icon-card">
|
|
112
|
+
<div class="icon-preview" id="icon-${name}">
|
|
113
|
+
<!-- Icon will be rendered here -->
|
|
114
|
+
<svg viewBox="0 0 24 24" fill="currentColor">
|
|
115
|
+
<text x="12" y="16" text-anchor="middle" font-size="8">${name.slice(3, 6)}</text>
|
|
116
|
+
</svg>
|
|
117
|
+
</div>
|
|
118
|
+
<div class="icon-name">${name}</div>
|
|
119
|
+
<div class="icon-actions">
|
|
120
|
+
<button onclick="copyName('${name}')" title="Copy name">📋</button>
|
|
121
|
+
<button onclick="copyImport('${name}')" title="Copy import">📥</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
`).join('\n');
|
|
125
|
+
|
|
126
|
+
return `<!DOCTYPE html>
|
|
127
|
+
<html lang="en">
|
|
128
|
+
<head>
|
|
129
|
+
<meta charset="UTF-8">
|
|
130
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
131
|
+
<title>${title} - @nice2dev/icons</title>
|
|
132
|
+
<style>
|
|
133
|
+
:root {
|
|
134
|
+
--bg: #0f172a;
|
|
135
|
+
--card-bg: #1e293b;
|
|
136
|
+
--text: #e2e8f0;
|
|
137
|
+
--text-dim: #94a3b8;
|
|
138
|
+
--accent: #3b82f6;
|
|
139
|
+
--accent-hover: #2563eb;
|
|
140
|
+
--success: #22c55e;
|
|
141
|
+
--border: #334155;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
* {
|
|
145
|
+
box-sizing: border-box;
|
|
146
|
+
margin: 0;
|
|
147
|
+
padding: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
body {
|
|
151
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
152
|
+
background: var(--bg);
|
|
153
|
+
color: var(--text);
|
|
154
|
+
min-height: 100vh;
|
|
155
|
+
padding: 2rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.header {
|
|
159
|
+
text-align: center;
|
|
160
|
+
margin-bottom: 2rem;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.header h1 {
|
|
164
|
+
font-size: 2rem;
|
|
165
|
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
|
166
|
+
-webkit-background-clip: text;
|
|
167
|
+
-webkit-text-fill-color: transparent;
|
|
168
|
+
background-clip: text;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.header p {
|
|
172
|
+
color: var(--text-dim);
|
|
173
|
+
margin-top: 0.5rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.controls {
|
|
177
|
+
display: flex;
|
|
178
|
+
gap: 1rem;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
margin-bottom: 2rem;
|
|
181
|
+
flex-wrap: wrap;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.controls input, .controls select {
|
|
185
|
+
padding: 0.75rem 1rem;
|
|
186
|
+
border: 1px solid var(--border);
|
|
187
|
+
border-radius: 0.5rem;
|
|
188
|
+
background: var(--card-bg);
|
|
189
|
+
color: var(--text);
|
|
190
|
+
font-size: 1rem;
|
|
191
|
+
min-width: 200px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.controls input:focus, .controls select:focus {
|
|
195
|
+
outline: none;
|
|
196
|
+
border-color: var(--accent);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.variant-buttons {
|
|
200
|
+
display: flex;
|
|
201
|
+
gap: 0.5rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.variant-btn {
|
|
205
|
+
padding: 0.5rem 1rem;
|
|
206
|
+
border: 1px solid var(--border);
|
|
207
|
+
border-radius: 0.375rem;
|
|
208
|
+
background: var(--card-bg);
|
|
209
|
+
color: var(--text);
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
transition: all 0.2s;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.variant-btn:hover {
|
|
215
|
+
border-color: var(--accent);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.variant-btn.active {
|
|
219
|
+
background: var(--accent);
|
|
220
|
+
border-color: var(--accent);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.icon-grid {
|
|
224
|
+
display: grid;
|
|
225
|
+
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
|
226
|
+
gap: 1rem;
|
|
227
|
+
max-width: 1400px;
|
|
228
|
+
margin: 0 auto;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.icon-card {
|
|
232
|
+
background: var(--card-bg);
|
|
233
|
+
border: 1px solid var(--border);
|
|
234
|
+
border-radius: 0.75rem;
|
|
235
|
+
padding: 1rem;
|
|
236
|
+
text-align: center;
|
|
237
|
+
transition: all 0.2s;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.icon-card:hover {
|
|
241
|
+
border-color: var(--accent);
|
|
242
|
+
transform: translateY(-2px);
|
|
243
|
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.icon-preview {
|
|
247
|
+
width: 48px;
|
|
248
|
+
height: 48px;
|
|
249
|
+
margin: 0 auto 0.75rem;
|
|
250
|
+
display: flex;
|
|
251
|
+
align-items: center;
|
|
252
|
+
justify-content: center;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.icon-preview svg {
|
|
256
|
+
width: 100%;
|
|
257
|
+
height: 100%;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.icon-name {
|
|
261
|
+
font-size: 0.75rem;
|
|
262
|
+
color: var(--text-dim);
|
|
263
|
+
word-break: break-all;
|
|
264
|
+
margin-bottom: 0.5rem;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.icon-actions {
|
|
268
|
+
display: flex;
|
|
269
|
+
gap: 0.5rem;
|
|
270
|
+
justify-content: center;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.icon-actions button {
|
|
274
|
+
background: none;
|
|
275
|
+
border: none;
|
|
276
|
+
cursor: pointer;
|
|
277
|
+
padding: 0.25rem;
|
|
278
|
+
font-size: 1rem;
|
|
279
|
+
opacity: 0.6;
|
|
280
|
+
transition: opacity 0.2s;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.icon-actions button:hover {
|
|
284
|
+
opacity: 1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.toast {
|
|
288
|
+
position: fixed;
|
|
289
|
+
bottom: 2rem;
|
|
290
|
+
left: 50%;
|
|
291
|
+
transform: translateX(-50%);
|
|
292
|
+
background: var(--success);
|
|
293
|
+
color: white;
|
|
294
|
+
padding: 0.75rem 1.5rem;
|
|
295
|
+
border-radius: 0.5rem;
|
|
296
|
+
font-weight: 500;
|
|
297
|
+
opacity: 0;
|
|
298
|
+
transition: opacity 0.3s;
|
|
299
|
+
pointer-events: none;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.toast.show {
|
|
303
|
+
opacity: 1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.stats {
|
|
307
|
+
text-align: center;
|
|
308
|
+
margin-bottom: 1.5rem;
|
|
309
|
+
color: var(--text-dim);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@media (prefers-color-scheme: light) {
|
|
313
|
+
:root {
|
|
314
|
+
--bg: #f8fafc;
|
|
315
|
+
--card-bg: #ffffff;
|
|
316
|
+
--text: #1e293b;
|
|
317
|
+
--text-dim: #64748b;
|
|
318
|
+
--border: #e2e8f0;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
</style>
|
|
322
|
+
</head>
|
|
323
|
+
<body>
|
|
324
|
+
<div class="header">
|
|
325
|
+
<h1>${title}</h1>
|
|
326
|
+
<p>@nice2dev/icons</p>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div class="controls">
|
|
330
|
+
<input type="text" id="search" placeholder="Search icons..." oninput="filterIcons()">
|
|
331
|
+
<select id="size" onchange="updateSize()">
|
|
332
|
+
<option value="24">24px</option>
|
|
333
|
+
<option value="32">32px</option>
|
|
334
|
+
<option value="48" selected>48px</option>
|
|
335
|
+
<option value="64">64px</option>
|
|
336
|
+
</select>
|
|
337
|
+
<input type="color" id="color" value="#3b82f6" onchange="updateColor()" title="Icon color">
|
|
338
|
+
<div class="variant-buttons">
|
|
339
|
+
<button class="variant-btn active" data-variant="solid" onclick="setVariant('solid')">Solid</button>
|
|
340
|
+
<button class="variant-btn" data-variant="outline" onclick="setVariant('outline')">Outline</button>
|
|
341
|
+
<button class="variant-btn" data-variant="duotone" onclick="setVariant('duotone')">Duotone</button>
|
|
342
|
+
<button class="variant-btn" data-variant="flat" onclick="setVariant('flat')">Flat</button>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<div class="stats" id="stats">${iconNames.length} icons</div>
|
|
347
|
+
|
|
348
|
+
<div class="icon-grid" id="icon-grid">
|
|
349
|
+
${icons}
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div class="toast" id="toast">Copied!</div>
|
|
353
|
+
|
|
354
|
+
<script>
|
|
355
|
+
let currentVariant = 'solid';
|
|
356
|
+
let currentColor = '#3b82f6';
|
|
357
|
+
let currentSize = 48;
|
|
358
|
+
|
|
359
|
+
function showToast(message) {
|
|
360
|
+
const toast = document.getElementById('toast');
|
|
361
|
+
toast.textContent = message;
|
|
362
|
+
toast.classList.add('show');
|
|
363
|
+
setTimeout(() => toast.classList.remove('show'), 2000);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function copyName(name) {
|
|
367
|
+
navigator.clipboard.writeText(name);
|
|
368
|
+
showToast('Name copied!');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function copyImport(name) {
|
|
372
|
+
const importStatement = \`import { \${name} } from '@nice2dev/icons';\`;
|
|
373
|
+
navigator.clipboard.writeText(importStatement);
|
|
374
|
+
showToast('Import copied!');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function filterIcons() {
|
|
378
|
+
const search = document.getElementById('search').value.toLowerCase();
|
|
379
|
+
const cards = document.querySelectorAll('.icon-card');
|
|
380
|
+
let visible = 0;
|
|
381
|
+
|
|
382
|
+
cards.forEach(card => {
|
|
383
|
+
const name = card.querySelector('.icon-name').textContent.toLowerCase();
|
|
384
|
+
const matches = name.includes(search);
|
|
385
|
+
card.style.display = matches ? '' : 'none';
|
|
386
|
+
if (matches) visible++;
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
document.getElementById('stats').textContent = visible + ' icons';
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function updateSize() {
|
|
393
|
+
currentSize = parseInt(document.getElementById('size').value);
|
|
394
|
+
document.querySelectorAll('.icon-preview').forEach(el => {
|
|
395
|
+
el.style.width = currentSize + 'px';
|
|
396
|
+
el.style.height = currentSize + 'px';
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function updateColor() {
|
|
401
|
+
currentColor = document.getElementById('color').value;
|
|
402
|
+
document.querySelectorAll('.icon-preview svg').forEach(svg => {
|
|
403
|
+
svg.style.color = currentColor;
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function setVariant(variant) {
|
|
408
|
+
currentVariant = variant;
|
|
409
|
+
document.querySelectorAll('.variant-btn').forEach(btn => {
|
|
410
|
+
btn.classList.toggle('active', btn.dataset.variant === variant);
|
|
411
|
+
});
|
|
412
|
+
// In a real implementation, this would update the icon rendering
|
|
413
|
+
}
|
|
414
|
+
</script>
|
|
415
|
+
</body>
|
|
416
|
+
</html>`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Command: preview
|
|
420
|
+
async function cmdPreview(args) {
|
|
421
|
+
const { positional, flags } = args;
|
|
422
|
+
const iconName = positional[0];
|
|
423
|
+
const port = parseInt(flags.port) || 3333;
|
|
424
|
+
|
|
425
|
+
log.title('📱 Icon Preview');
|
|
426
|
+
|
|
427
|
+
const { icons, categories } = await getIconList();
|
|
428
|
+
|
|
429
|
+
let iconsToShow;
|
|
430
|
+
let title;
|
|
431
|
+
|
|
432
|
+
if (iconName) {
|
|
433
|
+
// Show specific icon(s)
|
|
434
|
+
const matchingIcons = icons.filter(i =>
|
|
435
|
+
i.name.toLowerCase().includes(iconName.toLowerCase())
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
if (matchingIcons.length === 0) {
|
|
439
|
+
log.error(`No icons found matching "${iconName}"`);
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
iconsToShow = matchingIcons.map(i => i.name);
|
|
444
|
+
title = `Preview: ${iconName}`;
|
|
445
|
+
log.info(`Found ${matchingIcons.length} icon(s) matching "${iconName}"`);
|
|
446
|
+
} else {
|
|
447
|
+
// Show all icons
|
|
448
|
+
iconsToShow = icons.map(i => i.name);
|
|
449
|
+
title = 'All Icons Preview';
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const html = generatePreviewHtml(iconsToShow, title);
|
|
453
|
+
|
|
454
|
+
// Create a simple HTTP server
|
|
455
|
+
const server = http.createServer((req, res) => {
|
|
456
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
457
|
+
res.end(html);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
server.listen(port, () => {
|
|
461
|
+
log.success(`Preview server running at http://localhost:${port}`);
|
|
462
|
+
log.info('Press Ctrl+C to stop');
|
|
463
|
+
|
|
464
|
+
// Try to open browser
|
|
465
|
+
const url = `http://localhost:${port}`;
|
|
466
|
+
const openCmd = process.platform === 'win32' ? 'start' :
|
|
467
|
+
process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
468
|
+
|
|
469
|
+
spawn(openCmd, [url], { shell: true, stdio: 'ignore' });
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Command: list
|
|
474
|
+
async function cmdList(args) {
|
|
475
|
+
const { flags } = args;
|
|
476
|
+
const category = flags.category;
|
|
477
|
+
const format = flags.format || 'table';
|
|
478
|
+
const json = flags.json;
|
|
479
|
+
|
|
480
|
+
log.title('📋 Icon List');
|
|
481
|
+
|
|
482
|
+
const { icons, categories } = await getIconList();
|
|
483
|
+
|
|
484
|
+
if (json) {
|
|
485
|
+
console.log(JSON.stringify(category ? { [category]: categories[category] } : categories, null, 2));
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (category) {
|
|
490
|
+
if (!categories[category]) {
|
|
491
|
+
log.error(`Category "${category}" not found`);
|
|
492
|
+
log.info(`Available categories: ${Object.keys(categories).join(', ')}`);
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
console.log(`\n${colors.bright}${category}${colors.reset} (${categories[category].length} icons)\n`);
|
|
497
|
+
categories[category].forEach(icon => {
|
|
498
|
+
console.log(` ${colors.cyan}•${colors.reset} ${icon}`);
|
|
499
|
+
});
|
|
500
|
+
} else {
|
|
501
|
+
// Show all categories with counts
|
|
502
|
+
console.log(`\n${colors.bright}Categories:${colors.reset}\n`);
|
|
503
|
+
|
|
504
|
+
for (const [cat, catIcons] of Object.entries(categories).sort()) {
|
|
505
|
+
console.log(` ${colors.cyan}${cat}${colors.reset} ${colors.dim}(${catIcons.length} icons)${colors.reset}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
console.log(`\n${colors.dim}Total: ${icons.length} icons in ${Object.keys(categories).length} categories${colors.reset}`);
|
|
509
|
+
console.log(`\n${colors.dim}Use --category=<name> to list icons in a specific category${colors.reset}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Command: search
|
|
514
|
+
async function cmdSearch(args) {
|
|
515
|
+
const { positional, flags } = args;
|
|
516
|
+
const query = positional.join(' ');
|
|
517
|
+
|
|
518
|
+
if (!query) {
|
|
519
|
+
log.error('Please provide a search query');
|
|
520
|
+
console.log('\nUsage: ntd-icons search <query>');
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
log.title(`🔍 Searching for "${query}"`);
|
|
525
|
+
|
|
526
|
+
const { icons } = await getIconList();
|
|
527
|
+
|
|
528
|
+
const results = icons.filter(icon =>
|
|
529
|
+
icon.name.toLowerCase().includes(query.toLowerCase()) ||
|
|
530
|
+
icon.category.toLowerCase().includes(query.toLowerCase())
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
if (results.length === 0) {
|
|
534
|
+
log.warn('No icons found');
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
console.log(`\nFound ${results.length} icon(s):\n`);
|
|
539
|
+
|
|
540
|
+
results.forEach(icon => {
|
|
541
|
+
console.log(` ${colors.cyan}${icon.name}${colors.reset} ${colors.dim}[${icon.category}]${colors.reset}`);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
if (flags.copy && results.length === 1) {
|
|
545
|
+
const importStatement = `import { ${results[0].name} } from '@nice2dev/icons';`;
|
|
546
|
+
// Note: clipboard not available in all environments
|
|
547
|
+
log.info(`Import: ${importStatement}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Command: export
|
|
552
|
+
async function cmdExport(args) {
|
|
553
|
+
const { flags } = args;
|
|
554
|
+
const format = flags.format || 'svg';
|
|
555
|
+
const iconNames = flags.icons?.split(',') || [];
|
|
556
|
+
const output = flags.output || './icons-export';
|
|
557
|
+
const all = flags.all;
|
|
558
|
+
|
|
559
|
+
log.title('📦 Export Icons');
|
|
560
|
+
|
|
561
|
+
const { icons, categories } = await getIconList();
|
|
562
|
+
|
|
563
|
+
let iconsToExport;
|
|
564
|
+
|
|
565
|
+
if (all) {
|
|
566
|
+
iconsToExport = icons;
|
|
567
|
+
} else if (iconNames.length > 0) {
|
|
568
|
+
iconsToExport = icons.filter(i => iconNames.includes(i.name));
|
|
569
|
+
} else {
|
|
570
|
+
log.error('Please specify icons to export with --icons=NtdHome,NtdUser or use --all');
|
|
571
|
+
process.exit(1);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
log.info(`Exporting ${iconsToExport.length} icons as ${format.toUpperCase()}`);
|
|
575
|
+
|
|
576
|
+
// Create output directory
|
|
577
|
+
if (!fs.existsSync(output)) {
|
|
578
|
+
fs.mkdirSync(output, { recursive: true });
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (format === 'json') {
|
|
582
|
+
// Export as JSON manifest
|
|
583
|
+
const manifest = {
|
|
584
|
+
version: '1.0.0',
|
|
585
|
+
generatedAt: new Date().toISOString(),
|
|
586
|
+
icons: iconsToExport.map(i => ({
|
|
587
|
+
name: i.name,
|
|
588
|
+
category: i.category,
|
|
589
|
+
import: `import { ${i.name} } from '@nice2dev/icons';`,
|
|
590
|
+
})),
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
const filePath = path.join(output, 'icons-manifest.json');
|
|
594
|
+
fs.writeFileSync(filePath, JSON.stringify(manifest, null, 2));
|
|
595
|
+
log.success(`Exported manifest to ${filePath}`);
|
|
596
|
+
} else if (format === 'ts' || format === 'typescript') {
|
|
597
|
+
// Generate TypeScript barrel file
|
|
598
|
+
const imports = iconsToExport.map(i => ` ${i.name},`).join('\n');
|
|
599
|
+
const content = `// Auto-generated icon barrel file
|
|
600
|
+
// Generated at ${new Date().toISOString()}
|
|
601
|
+
|
|
602
|
+
export {
|
|
603
|
+
${imports}
|
|
604
|
+
} from '@nice2dev/icons';
|
|
605
|
+
`;
|
|
606
|
+
|
|
607
|
+
const filePath = path.join(output, 'icons.ts');
|
|
608
|
+
fs.writeFileSync(filePath, content);
|
|
609
|
+
log.success(`Exported TypeScript barrel to ${filePath}`);
|
|
610
|
+
} else {
|
|
611
|
+
log.warn(`Format "${format}" export requires the full build. Use JSON or TypeScript for now.`);
|
|
612
|
+
log.info('For SVG export, use: npm run build:spritesheet');
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Command: info
|
|
617
|
+
async function cmdInfo(args) {
|
|
618
|
+
const { positional } = args;
|
|
619
|
+
const iconName = positional[0];
|
|
620
|
+
|
|
621
|
+
if (!iconName) {
|
|
622
|
+
log.error('Please specify an icon name');
|
|
623
|
+
console.log('\nUsage: ntd-icons info <icon-name>');
|
|
624
|
+
process.exit(1);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
log.title(`ℹ️ Icon Info: ${iconName}`);
|
|
628
|
+
|
|
629
|
+
const { icons } = await getIconList();
|
|
630
|
+
const icon = icons.find(i => i.name.toLowerCase() === iconName.toLowerCase());
|
|
631
|
+
|
|
632
|
+
if (!icon) {
|
|
633
|
+
log.error(`Icon "${iconName}" not found`);
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
console.log(`
|
|
638
|
+
${colors.bright}Name:${colors.reset} ${icon.name}
|
|
639
|
+
${colors.bright}Category:${colors.reset} ${icon.category}
|
|
640
|
+
${colors.bright}File:${colors.reset} src/icons/${icon.file}
|
|
641
|
+
|
|
642
|
+
${colors.bright}Import:${colors.reset}
|
|
643
|
+
import { ${icon.name} } from '@nice2dev/icons';
|
|
644
|
+
|
|
645
|
+
${colors.bright}Usage:${colors.reset}
|
|
646
|
+
<${icon.name} size="md" variant="solid" primaryColor="#3b82f6" />
|
|
647
|
+
<${icon.name} variant="duotone" animation="pulse" />
|
|
648
|
+
|
|
649
|
+
${colors.bright}Variants:${colors.reset} solid, outline, duotone, flat
|
|
650
|
+
${colors.bright}Sizes:${colors.reset} xs (12px), sm (16px), md (24px), lg (32px), xl (48px)
|
|
651
|
+
`);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Show help
|
|
655
|
+
function showHelp() {
|
|
656
|
+
console.log(`
|
|
657
|
+
${colors.bright}${colors.cyan}@nice2dev/icons CLI${colors.reset}
|
|
658
|
+
|
|
659
|
+
${colors.bright}Usage:${colors.reset} ntd-icons <command> [options]
|
|
660
|
+
|
|
661
|
+
${colors.bright}Commands:${colors.reset}
|
|
662
|
+
${colors.cyan}preview${colors.reset} [icon] Open icon preview in browser
|
|
663
|
+
${colors.cyan}list${colors.reset} List all icons and categories
|
|
664
|
+
${colors.cyan}search${colors.reset} <query> Search icons by name
|
|
665
|
+
${colors.cyan}export${colors.reset} Export icons to various formats
|
|
666
|
+
${colors.cyan}info${colors.reset} <icon> Show detailed info about an icon
|
|
667
|
+
|
|
668
|
+
${colors.bright}Options:${colors.reset}
|
|
669
|
+
${colors.cyan}--category${colors.reset}=<name> Filter by category (list command)
|
|
670
|
+
${colors.cyan}--format${colors.reset}=<type> Export format: json, ts (export command)
|
|
671
|
+
${colors.cyan}--icons${colors.reset}=<names> Comma-separated icon names (export command)
|
|
672
|
+
${colors.cyan}--all${colors.reset} Export all icons (export command)
|
|
673
|
+
${colors.cyan}--output${colors.reset}=<path> Output directory (export command)
|
|
674
|
+
${colors.cyan}--port${colors.reset}=<number> Server port (preview command, default: 3333)
|
|
675
|
+
${colors.cyan}--json${colors.reset} Output as JSON
|
|
676
|
+
${colors.cyan}--help, -h${colors.reset} Show this help
|
|
677
|
+
|
|
678
|
+
${colors.bright}Examples:${colors.reset}
|
|
679
|
+
${colors.dim}# Preview all icons${colors.reset}
|
|
680
|
+
npx @nice2dev/icons preview
|
|
681
|
+
|
|
682
|
+
${colors.dim}# Preview icons matching "home"${colors.reset}
|
|
683
|
+
npx @nice2dev/icons preview home
|
|
684
|
+
|
|
685
|
+
${colors.dim}# List icons in UI category${colors.reset}
|
|
686
|
+
npx @nice2dev/icons list --category=ui
|
|
687
|
+
|
|
688
|
+
${colors.dim}# Search for icons${colors.reset}
|
|
689
|
+
npx @nice2dev/icons search arrow
|
|
690
|
+
|
|
691
|
+
${colors.dim}# Export specific icons to TypeScript${colors.reset}
|
|
692
|
+
npx @nice2dev/icons export --format=ts --icons=NtdHome,NtdUser
|
|
693
|
+
|
|
694
|
+
${colors.dim}# Get info about an icon${colors.reset}
|
|
695
|
+
npx @nice2dev/icons info NtdHome
|
|
696
|
+
`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Main entry point
|
|
700
|
+
async function main() {
|
|
701
|
+
const args = parseArgs(process.argv.slice(2));
|
|
702
|
+
|
|
703
|
+
if (args.flags.help || args.flags.h || !args.command) {
|
|
704
|
+
showHelp();
|
|
705
|
+
process.exit(args.command ? 0 : 1);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const commands = {
|
|
709
|
+
preview: cmdPreview,
|
|
710
|
+
list: cmdList,
|
|
711
|
+
search: cmdSearch,
|
|
712
|
+
export: cmdExport,
|
|
713
|
+
info: cmdInfo,
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
const handler = commands[args.command];
|
|
717
|
+
|
|
718
|
+
if (!handler) {
|
|
719
|
+
log.error(`Unknown command: ${args.command}`);
|
|
720
|
+
showHelp();
|
|
721
|
+
process.exit(1);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
try {
|
|
725
|
+
await handler(args);
|
|
726
|
+
} catch (err) {
|
|
727
|
+
log.error(`Command failed: ${err.message}`);
|
|
728
|
+
process.exit(1);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
main();
|