@nice2dev/icons 1.3.15 → 1.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/cjs/NiceDesktopIconExporter.js +688 -599
- package/dist/cjs/NiceFaviconGenerator.js +603 -417
- package/dist/cjs/NiceIconPicker.js +788 -508
- package/dist/cjs/NiceIconPreview.js +246 -188
- package/dist/cjs/NiceIconSetCreator.js +683 -552
- package/dist/cjs/NiceIconStacker.js +547 -171
- package/dist/cjs/NiceIconValue.js +27 -35
- package/dist/cjs/NtdIconBadge.js +384 -360
- package/dist/cjs/NtdIconMorph.js +419 -354
- package/dist/cjs/NtdStateMorph.js +150 -147
- package/dist/cjs/advancedIconSearch.js +373 -510
- package/dist/cjs/animationControls.js +518 -642
- package/dist/cjs/client.js +12 -15
- package/dist/cjs/createIcon.js +349 -251
- package/dist/cjs/design-tokens.js +324 -390
- package/dist/cjs/figmaExport.js +162 -257
- package/dist/cjs/figmaSync.js +234 -316
- package/dist/cjs/headless-ui.js +240 -368
- package/dist/cjs/iconAnalytics.js +337 -393
- package/dist/cjs/iconHtml.js +64 -86
- package/dist/cjs/iconMetadata.js +392 -472
- package/dist/cjs/iconParts.js +120 -90
- package/dist/cjs/iconStack.js +136 -155
- package/dist/cjs/iconStyles.js +1609 -504
- package/dist/cjs/iconValue.js +359 -430
- package/dist/cjs/icons/accessibility.js +1119 -134
- package/dist/cjs/icons/actions.js +1290 -330
- package/dist/cjs/icons/ai.js +1424 -144
- package/dist/cjs/icons/brand.js +1344 -172
- package/dist/cjs/icons/business.js +903 -193
- package/dist/cjs/icons/devtech.js +1656 -227
- package/dist/cjs/icons/ecommerce.js +802 -111
- package/dist/cjs/icons/education.js +1345 -199
- package/dist/cjs/icons/files.js +1597 -328
- package/dist/cjs/icons/fintech.js +793 -104
- package/dist/cjs/icons/food.js +1507 -153
- package/dist/cjs/icons/fun.js +786 -109
- package/dist/cjs/icons/gaming.js +916 -170
- package/dist/cjs/icons/health.js +623 -112
- package/dist/cjs/icons/index.js +32 -35
- package/dist/cjs/icons/input.js +282 -83
- package/dist/cjs/icons/legal.js +1065 -122
- package/dist/cjs/icons/media.js +1061 -267
- package/dist/cjs/icons/navigation.js +387 -122
- package/dist/cjs/icons/omniverk.js +747 -90
- package/dist/cjs/icons/realestate.js +1520 -193
- package/dist/cjs/icons/saas.js +867 -98
- package/dist/cjs/icons/science.js +1451 -202
- package/dist/cjs/icons/shapes.js +1316 -308
- package/dist/cjs/icons/smarthome.js +786 -90
- package/dist/cjs/icons/social.js +591 -59
- package/dist/cjs/icons/sports.js +1225 -133
- package/dist/cjs/icons/sustainability.js +1211 -139
- package/dist/cjs/icons/travel.js +1470 -148
- package/dist/cjs/icons/ui.js +1508 -315
- package/dist/cjs/icons/weather.js +1160 -173
- package/dist/cjs/index.js +566 -568
- package/dist/cjs/lottieIntegration.js +261 -266
- package/dist/cjs/nicetodev-icons.css +1 -1
- package/dist/cjs/particleEffects.js +270 -251
- package/dist/cjs/resolver.js +229 -284
- package/dist/cjs/rsc.js +175 -193
- package/dist/cjs/sharpen.js +73 -130
- package/dist/cjs/smilAnimations.js +371 -165
- package/dist/cjs/stacking.js +87 -122
- package/dist/cjs/tailwind-plugin.js +280 -320
- package/dist/cjs/types.js +46 -72
- package/dist/cjs/utilities.js +163 -324
- package/dist/cjs/variantProps.js +12 -52
- package/dist/esm/NiceDesktopIconExporter.js +671 -598
- package/dist/esm/NiceFaviconGenerator.js +583 -416
- package/dist/esm/NiceIconPicker.js +789 -507
- package/dist/esm/NiceIconPreview.js +247 -187
- package/dist/esm/NiceIconSetCreator.js +685 -551
- package/dist/esm/NiceIconStacker.js +549 -170
- package/dist/esm/NiceIconValue.js +29 -34
- package/dist/esm/NtdIconBadge.js +388 -359
- package/dist/esm/NtdIconMorph.js +423 -353
- package/dist/esm/NtdStateMorph.js +152 -146
- package/dist/esm/advancedIconSearch.js +391 -509
- package/dist/esm/animationControls.js +527 -641
- package/dist/esm/client.js +48 -10
- package/dist/esm/createIcon.js +350 -250
- package/dist/esm/design-tokens.js +334 -387
- package/dist/esm/figmaExport.js +170 -256
- package/dist/esm/figmaSync.js +237 -315
- package/dist/esm/headless-ui.js +254 -365
- package/dist/esm/iconAnalytics.js +342 -392
- package/dist/esm/iconHtml.js +67 -85
- package/dist/esm/iconMetadata.js +404 -471
- package/dist/esm/iconParts.js +125 -89
- package/dist/esm/iconStack.js +145 -154
- package/dist/esm/iconStyles.js +1620 -503
- package/dist/esm/iconValue.js +361 -429
- package/dist/esm/icons/accessibility.js +1133 -133
- package/dist/esm/icons/actions.js +1320 -329
- package/dist/esm/icons/ai.js +1441 -143
- package/dist/esm/icons/brand.js +1351 -171
- package/dist/esm/icons/business.js +931 -201
- package/dist/esm/icons/devtech.js +1687 -226
- package/dist/esm/icons/ecommerce.js +816 -110
- package/dist/esm/icons/education.js +1364 -198
- package/dist/esm/icons/files.js +1628 -327
- package/dist/esm/icons/fintech.js +807 -103
- package/dist/esm/icons/food.js +1524 -152
- package/dist/esm/icons/fun.js +801 -108
- package/dist/esm/icons/gaming.js +933 -169
- package/dist/esm/icons/health.js +636 -111
- package/dist/esm/icons/index.js +596 -32
- package/dist/esm/icons/input.js +326 -82
- package/dist/esm/icons/legal.js +1079 -121
- package/dist/esm/icons/media.js +1086 -266
- package/dist/esm/icons/navigation.js +401 -121
- package/dist/esm/icons/omniverk.js +759 -89
- package/dist/esm/icons/realestate.js +1544 -198
- package/dist/esm/icons/saas.js +880 -97
- package/dist/esm/icons/science.js +1477 -208
- package/dist/esm/icons/shapes.js +1343 -307
- package/dist/esm/icons/smarthome.js +799 -89
- package/dist/esm/icons/social.js +599 -58
- package/dist/esm/icons/sports.js +1240 -132
- package/dist/esm/icons/sustainability.js +1226 -138
- package/dist/esm/icons/travel.js +1486 -147
- package/dist/esm/icons/ui.js +1540 -314
- package/dist/esm/icons/weather.js +1181 -172
- package/dist/esm/index.js +756 -58
- package/dist/esm/lottieIntegration.js +264 -265
- package/dist/esm/nicetodev-icons.css +1 -1
- package/dist/esm/particleEffects.js +271 -250
- package/dist/esm/resolver.js +231 -283
- package/dist/esm/rsc.js +182 -192
- package/dist/esm/sharpen.js +78 -129
- package/dist/esm/smilAnimations.js +376 -164
- package/dist/esm/stacking.js +99 -121
- package/dist/esm/tailwind-plugin.js +282 -319
- package/dist/esm/types.js +50 -71
- package/dist/esm/utilities.js +177 -321
- package/dist/esm/variantProps.js +23 -52
- package/package.json +15 -22
|
@@ -1,655 +1,744 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
25
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
26
|
+
const uiCore = require("@nice2dev/ui-core");
|
|
27
|
+
const React = require("react");
|
|
28
|
+
;/* empty css */
|
|
11
29
|
const DESKTOP_ICON_SIZES = [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
{ size: 16, label: "16×16", platforms: ["windows", "linux"] },
|
|
31
|
+
{ size: 24, label: "24×24", platforms: ["linux"] },
|
|
32
|
+
{ size: 32, label: "32×32", platforms: ["windows", "linux"] },
|
|
33
|
+
{ size: 48, label: "48×48", platforms: ["windows", "linux"] },
|
|
34
|
+
{ size: 64, label: "64×64", platforms: ["windows", "linux"] },
|
|
35
|
+
{ size: 128, label: "128×128", platforms: ["windows", "macos", "linux"] },
|
|
36
|
+
{ size: 256, label: "256×256", platforms: ["windows", "macos", "linux"] },
|
|
37
|
+
{ size: 512, label: "512×512", platforms: ["macos", "linux"] },
|
|
38
|
+
{ size: 1024, label: "1024×1024", platforms: ["macos"] }
|
|
21
39
|
];
|
|
22
|
-
/** Retina/HiDPI scale factors */
|
|
23
40
|
const RETINA_VARIANTS = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
{ scale: 1, suffix: "" },
|
|
42
|
+
{ scale: 2, suffix: "@2x" },
|
|
43
|
+
{ scale: 3, suffix: "@3x" }
|
|
27
44
|
];
|
|
28
|
-
/** Default export configuration */
|
|
29
45
|
const DEFAULT_CONFIG = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
name: "icon",
|
|
47
|
+
includeWindows: true,
|
|
48
|
+
includeMacOS: true,
|
|
49
|
+
includeLinux: true,
|
|
50
|
+
includeRetina: true,
|
|
51
|
+
backgroundColor: void 0,
|
|
52
|
+
// transparent
|
|
53
|
+
padding: 0.1,
|
|
54
|
+
fillColor: "#1e1e1e",
|
|
55
|
+
sizes: [16, 32, 48, 64, 128, 256, 512]
|
|
39
56
|
};
|
|
40
|
-
/** Linux hicolor theme icon directories */
|
|
41
57
|
const LINUX_HICOLOR_DIRS = [
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
{ size: 16, dir: "hicolor/16x16/apps" },
|
|
59
|
+
{ size: 24, dir: "hicolor/24x24/apps" },
|
|
60
|
+
{ size: 32, dir: "hicolor/32x32/apps" },
|
|
61
|
+
{ size: 48, dir: "hicolor/48x48/apps" },
|
|
62
|
+
{ size: 64, dir: "hicolor/64x64/apps" },
|
|
63
|
+
{ size: 128, dir: "hicolor/128x128/apps" },
|
|
64
|
+
{ size: 256, dir: "hicolor/256x256/apps" },
|
|
65
|
+
{ size: 512, dir: "hicolor/512x512/apps" }
|
|
50
66
|
];
|
|
51
|
-
/* ══════════════════════════════════════════════════════════════════════════════
|
|
52
|
-
Utility Functions
|
|
53
|
-
══════════════════════════════════════════════════════════════════════════════ */
|
|
54
|
-
/**
|
|
55
|
-
* Render an icon component to a canvas at the specified size
|
|
56
|
-
*/
|
|
57
67
|
async function renderIconToCanvas(IconComponent, size, fillColor, backgroundColor, padding = 0.1) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
root.unmount();
|
|
97
|
-
resolve(canvas);
|
|
98
|
-
}
|
|
99
|
-
}, 50);
|
|
100
|
-
});
|
|
68
|
+
const canvas = document.createElement("canvas");
|
|
69
|
+
canvas.width = size;
|
|
70
|
+
canvas.height = size;
|
|
71
|
+
const ctx = canvas.getContext("2d");
|
|
72
|
+
if (backgroundColor) {
|
|
73
|
+
ctx.fillStyle = backgroundColor;
|
|
74
|
+
ctx.fillRect(0, 0, size, size);
|
|
75
|
+
} else {
|
|
76
|
+
ctx.clearRect(0, 0, size, size);
|
|
77
|
+
}
|
|
78
|
+
const container = document.createElement("div");
|
|
79
|
+
const iconSize = Math.round(size * (1 - padding * 2));
|
|
80
|
+
const offset = Math.round(size * padding);
|
|
81
|
+
const { createRoot } = await import("react-dom/client");
|
|
82
|
+
const root = createRoot(container);
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
root.render(/* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: iconSize, fill: fillColor }));
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
const svg = container.querySelector("svg");
|
|
87
|
+
if (svg) {
|
|
88
|
+
const svgData = new XMLSerializer().serializeToString(svg);
|
|
89
|
+
const img = new Image();
|
|
90
|
+
img.onload = () => {
|
|
91
|
+
ctx.drawImage(img, offset, offset, iconSize, iconSize);
|
|
92
|
+
root.unmount();
|
|
93
|
+
resolve(canvas);
|
|
94
|
+
};
|
|
95
|
+
img.onerror = () => {
|
|
96
|
+
root.unmount();
|
|
97
|
+
resolve(canvas);
|
|
98
|
+
};
|
|
99
|
+
img.src = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(svgData)));
|
|
100
|
+
} else {
|
|
101
|
+
root.unmount();
|
|
102
|
+
resolve(canvas);
|
|
103
|
+
}
|
|
104
|
+
}, 50);
|
|
105
|
+
});
|
|
101
106
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Convert canvas to PNG blob
|
|
104
|
-
*/
|
|
105
107
|
function canvasToBlob(canvas) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
canvas.toBlob((blob) => {
|
|
110
|
+
if (blob) {
|
|
111
|
+
resolve(blob);
|
|
112
|
+
} else {
|
|
113
|
+
reject(new Error("Failed to convert canvas to blob"));
|
|
114
|
+
}
|
|
115
|
+
}, "image/png");
|
|
116
|
+
});
|
|
116
117
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Create a Windows ICO file from multiple PNG images.
|
|
119
|
-
* ICO format: https://en.wikipedia.org/wiki/ICO_(file_format)
|
|
120
|
-
*/
|
|
121
118
|
async function createIcoFile(pngBuffers) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
view.
|
|
143
|
-
view.
|
|
144
|
-
view.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
view.setUint32(entryOffset + 12, entry.offset, true); // Image data offset
|
|
157
|
-
entryOffset += entrySize;
|
|
158
|
-
}
|
|
159
|
-
// Write image data
|
|
160
|
-
for (const entry of entries) {
|
|
161
|
-
uint8.set(new Uint8Array(entry.data), entry.offset);
|
|
162
|
-
}
|
|
163
|
-
return new Blob([buffer], { type: 'image/x-icon' });
|
|
119
|
+
const sorted = [...pngBuffers].sort((a, b) => b.size - a.size);
|
|
120
|
+
const headerSize = 6;
|
|
121
|
+
const entrySize = 16;
|
|
122
|
+
const directorySize = headerSize + entrySize * sorted.length;
|
|
123
|
+
let currentOffset = directorySize;
|
|
124
|
+
const entries = [];
|
|
125
|
+
for (const png of sorted) {
|
|
126
|
+
entries.push({ size: png.size, offset: currentOffset, data: png.data });
|
|
127
|
+
currentOffset += png.data.byteLength;
|
|
128
|
+
}
|
|
129
|
+
const totalSize = currentOffset;
|
|
130
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
131
|
+
const view = new DataView(buffer);
|
|
132
|
+
const uint8 = new Uint8Array(buffer);
|
|
133
|
+
view.setUint16(0, 0, true);
|
|
134
|
+
view.setUint16(2, 1, true);
|
|
135
|
+
view.setUint16(4, sorted.length, true);
|
|
136
|
+
let entryOffset = headerSize;
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
const size = entry.size > 255 ? 0 : entry.size;
|
|
139
|
+
view.setUint8(entryOffset + 0, size);
|
|
140
|
+
view.setUint8(entryOffset + 1, size);
|
|
141
|
+
view.setUint8(entryOffset + 2, 0);
|
|
142
|
+
view.setUint8(entryOffset + 3, 0);
|
|
143
|
+
view.setUint16(entryOffset + 4, 1, true);
|
|
144
|
+
view.setUint16(entryOffset + 6, 32, true);
|
|
145
|
+
view.setUint32(entryOffset + 8, entry.data.byteLength, true);
|
|
146
|
+
view.setUint32(entryOffset + 12, entry.offset, true);
|
|
147
|
+
entryOffset += entrySize;
|
|
148
|
+
}
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
uint8.set(new Uint8Array(entry.data), entry.offset);
|
|
151
|
+
}
|
|
152
|
+
return new Blob([buffer], { type: "image/x-icon" });
|
|
164
153
|
}
|
|
165
|
-
/**
|
|
166
|
-
* Create a ZIP file containing multiple files
|
|
167
|
-
*/
|
|
168
154
|
async function createZipFile(files) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
else if (file.data instanceof Blob) {
|
|
182
|
-
dataBytes = new Uint8Array(await file.data.arrayBuffer());
|
|
183
|
-
}
|
|
184
|
-
else {
|
|
185
|
-
dataBytes = new Uint8Array(file.data);
|
|
186
|
-
}
|
|
187
|
-
const crc32 = calculateCRC32(dataBytes);
|
|
188
|
-
fileEntries.push({ name: nameBytes, data: dataBytes, crc32, offset });
|
|
189
|
-
// Local file header size: 30 + name length + data length
|
|
190
|
-
offset += 30 + nameBytes.length + dataBytes.length;
|
|
155
|
+
const encoder = new TextEncoder();
|
|
156
|
+
const fileEntries = [];
|
|
157
|
+
let offset = 0;
|
|
158
|
+
for (const file of files) {
|
|
159
|
+
const nameBytes = encoder.encode(file.name);
|
|
160
|
+
let dataBytes;
|
|
161
|
+
if (typeof file.data === "string") {
|
|
162
|
+
dataBytes = encoder.encode(file.data);
|
|
163
|
+
} else if (file.data instanceof Blob) {
|
|
164
|
+
dataBytes = new Uint8Array(await file.data.arrayBuffer());
|
|
165
|
+
} else {
|
|
166
|
+
dataBytes = new Uint8Array(file.data);
|
|
191
167
|
}
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
pos += 4;
|
|
209
|
-
// Version needed
|
|
210
|
-
view.setUint16(pos, 20, true);
|
|
211
|
-
pos += 2;
|
|
212
|
-
// General purpose bit flag
|
|
213
|
-
view.setUint16(pos, 0, true);
|
|
214
|
-
pos += 2;
|
|
215
|
-
// Compression method (0 = store)
|
|
216
|
-
view.setUint16(pos, 0, true);
|
|
217
|
-
pos += 2;
|
|
218
|
-
// File time & date (use current)
|
|
219
|
-
const now = new Date();
|
|
220
|
-
const dosTime = ((now.getHours() << 11) | (now.getMinutes() << 5) | (now.getSeconds() >> 1)) & 0xffff;
|
|
221
|
-
const dosDate = (((now.getFullYear() - 1980) << 9) | ((now.getMonth() + 1) << 5) | now.getDate()) & 0xffff;
|
|
222
|
-
view.setUint16(pos, dosTime, true);
|
|
223
|
-
pos += 2;
|
|
224
|
-
view.setUint16(pos, dosDate, true);
|
|
225
|
-
pos += 2;
|
|
226
|
-
// CRC-32
|
|
227
|
-
view.setUint32(pos, entry.crc32, true);
|
|
228
|
-
pos += 4;
|
|
229
|
-
// Compressed size
|
|
230
|
-
view.setUint32(pos, entry.data.length, true);
|
|
231
|
-
pos += 4;
|
|
232
|
-
// Uncompressed size
|
|
233
|
-
view.setUint32(pos, entry.data.length, true);
|
|
234
|
-
pos += 4;
|
|
235
|
-
// File name length
|
|
236
|
-
view.setUint16(pos, entry.name.length, true);
|
|
237
|
-
pos += 2;
|
|
238
|
-
// Extra field length
|
|
239
|
-
view.setUint16(pos, 0, true);
|
|
240
|
-
pos += 2;
|
|
241
|
-
// File name
|
|
242
|
-
bytes.set(entry.name, pos);
|
|
243
|
-
pos += entry.name.length;
|
|
244
|
-
// File data
|
|
245
|
-
bytes.set(entry.data, pos);
|
|
246
|
-
pos += entry.data.length;
|
|
247
|
-
}
|
|
248
|
-
// Write central directory
|
|
249
|
-
for (const entry of fileEntries) {
|
|
250
|
-
// Central directory file header signature
|
|
251
|
-
view.setUint32(pos, 0x02014b50, true);
|
|
252
|
-
pos += 4;
|
|
253
|
-
// Version made by
|
|
254
|
-
view.setUint16(pos, 20, true);
|
|
255
|
-
pos += 2;
|
|
256
|
-
// Version needed
|
|
257
|
-
view.setUint16(pos, 20, true);
|
|
258
|
-
pos += 2;
|
|
259
|
-
// General purpose bit flag
|
|
260
|
-
view.setUint16(pos, 0, true);
|
|
261
|
-
pos += 2;
|
|
262
|
-
// Compression method
|
|
263
|
-
view.setUint16(pos, 0, true);
|
|
264
|
-
pos += 2;
|
|
265
|
-
// File time & date
|
|
266
|
-
const now = new Date();
|
|
267
|
-
const dosTime = ((now.getHours() << 11) | (now.getMinutes() << 5) | (now.getSeconds() >> 1)) & 0xffff;
|
|
268
|
-
const dosDate = (((now.getFullYear() - 1980) << 9) | ((now.getMonth() + 1) << 5) | now.getDate()) & 0xffff;
|
|
269
|
-
view.setUint16(pos, dosTime, true);
|
|
270
|
-
pos += 2;
|
|
271
|
-
view.setUint16(pos, dosDate, true);
|
|
272
|
-
pos += 2;
|
|
273
|
-
// CRC-32
|
|
274
|
-
view.setUint32(pos, entry.crc32, true);
|
|
275
|
-
pos += 4;
|
|
276
|
-
// Compressed size
|
|
277
|
-
view.setUint32(pos, entry.data.length, true);
|
|
278
|
-
pos += 4;
|
|
279
|
-
// Uncompressed size
|
|
280
|
-
view.setUint32(pos, entry.data.length, true);
|
|
281
|
-
pos += 4;
|
|
282
|
-
// File name length
|
|
283
|
-
view.setUint16(pos, entry.name.length, true);
|
|
284
|
-
pos += 2;
|
|
285
|
-
// Extra field length
|
|
286
|
-
view.setUint16(pos, 0, true);
|
|
287
|
-
pos += 2;
|
|
288
|
-
// File comment length
|
|
289
|
-
view.setUint16(pos, 0, true);
|
|
290
|
-
pos += 2;
|
|
291
|
-
// Disk number start
|
|
292
|
-
view.setUint16(pos, 0, true);
|
|
293
|
-
pos += 2;
|
|
294
|
-
// Internal file attributes
|
|
295
|
-
view.setUint16(pos, 0, true);
|
|
296
|
-
pos += 2;
|
|
297
|
-
// External file attributes
|
|
298
|
-
view.setUint32(pos, 0, true);
|
|
299
|
-
pos += 4;
|
|
300
|
-
// Relative offset of local header
|
|
301
|
-
view.setUint32(pos, entry.offset, true);
|
|
302
|
-
pos += 4;
|
|
303
|
-
// File name
|
|
304
|
-
bytes.set(entry.name, pos);
|
|
305
|
-
pos += entry.name.length;
|
|
306
|
-
}
|
|
307
|
-
// End of central directory record
|
|
308
|
-
view.setUint32(pos, 0x06054b50, true);
|
|
168
|
+
const crc32 = calculateCRC32(dataBytes);
|
|
169
|
+
fileEntries.push({ name: nameBytes, data: dataBytes, crc32, offset });
|
|
170
|
+
offset += 30 + nameBytes.length + dataBytes.length;
|
|
171
|
+
}
|
|
172
|
+
const centralDirectoryOffset = offset;
|
|
173
|
+
let centralDirSize = 0;
|
|
174
|
+
for (const entry of fileEntries) {
|
|
175
|
+
centralDirSize += 46 + entry.name.length;
|
|
176
|
+
}
|
|
177
|
+
const totalSize = offset + centralDirSize + 22;
|
|
178
|
+
const buffer = new ArrayBuffer(totalSize);
|
|
179
|
+
const view = new DataView(buffer);
|
|
180
|
+
const bytes = new Uint8Array(buffer);
|
|
181
|
+
let pos = 0;
|
|
182
|
+
for (const entry of fileEntries) {
|
|
183
|
+
view.setUint32(pos, 67324752, true);
|
|
309
184
|
pos += 4;
|
|
310
|
-
|
|
185
|
+
view.setUint16(pos, 20, true);
|
|
186
|
+
pos += 2;
|
|
311
187
|
view.setUint16(pos, 0, true);
|
|
312
188
|
pos += 2;
|
|
313
|
-
// Disk number with CD
|
|
314
189
|
view.setUint16(pos, 0, true);
|
|
315
190
|
pos += 2;
|
|
316
|
-
|
|
317
|
-
|
|
191
|
+
const now = /* @__PURE__ */ new Date();
|
|
192
|
+
const dosTime = (now.getHours() << 11 | now.getMinutes() << 5 | now.getSeconds() >> 1) & 65535;
|
|
193
|
+
const dosDate = (now.getFullYear() - 1980 << 9 | now.getMonth() + 1 << 5 | now.getDate()) & 65535;
|
|
194
|
+
view.setUint16(pos, dosTime, true);
|
|
318
195
|
pos += 2;
|
|
319
|
-
|
|
320
|
-
view.setUint16(pos, fileEntries.length, true);
|
|
196
|
+
view.setUint16(pos, dosDate, true);
|
|
321
197
|
pos += 2;
|
|
322
|
-
|
|
323
|
-
|
|
198
|
+
view.setUint32(pos, entry.crc32, true);
|
|
199
|
+
pos += 4;
|
|
200
|
+
view.setUint32(pos, entry.data.length, true);
|
|
324
201
|
pos += 4;
|
|
325
|
-
|
|
326
|
-
view.setUint32(pos, centralDirectoryOffset, true);
|
|
202
|
+
view.setUint32(pos, entry.data.length, true);
|
|
327
203
|
pos += 4;
|
|
328
|
-
|
|
204
|
+
view.setUint16(pos, entry.name.length, true);
|
|
205
|
+
pos += 2;
|
|
206
|
+
view.setUint16(pos, 0, true);
|
|
207
|
+
pos += 2;
|
|
208
|
+
bytes.set(entry.name, pos);
|
|
209
|
+
pos += entry.name.length;
|
|
210
|
+
bytes.set(entry.data, pos);
|
|
211
|
+
pos += entry.data.length;
|
|
212
|
+
}
|
|
213
|
+
for (const entry of fileEntries) {
|
|
214
|
+
view.setUint32(pos, 33639248, true);
|
|
215
|
+
pos += 4;
|
|
216
|
+
view.setUint16(pos, 20, true);
|
|
217
|
+
pos += 2;
|
|
218
|
+
view.setUint16(pos, 20, true);
|
|
219
|
+
pos += 2;
|
|
329
220
|
view.setUint16(pos, 0, true);
|
|
330
|
-
|
|
221
|
+
pos += 2;
|
|
222
|
+
view.setUint16(pos, 0, true);
|
|
223
|
+
pos += 2;
|
|
224
|
+
const now = /* @__PURE__ */ new Date();
|
|
225
|
+
const dosTime = (now.getHours() << 11 | now.getMinutes() << 5 | now.getSeconds() >> 1) & 65535;
|
|
226
|
+
const dosDate = (now.getFullYear() - 1980 << 9 | now.getMonth() + 1 << 5 | now.getDate()) & 65535;
|
|
227
|
+
view.setUint16(pos, dosTime, true);
|
|
228
|
+
pos += 2;
|
|
229
|
+
view.setUint16(pos, dosDate, true);
|
|
230
|
+
pos += 2;
|
|
231
|
+
view.setUint32(pos, entry.crc32, true);
|
|
232
|
+
pos += 4;
|
|
233
|
+
view.setUint32(pos, entry.data.length, true);
|
|
234
|
+
pos += 4;
|
|
235
|
+
view.setUint32(pos, entry.data.length, true);
|
|
236
|
+
pos += 4;
|
|
237
|
+
view.setUint16(pos, entry.name.length, true);
|
|
238
|
+
pos += 2;
|
|
239
|
+
view.setUint16(pos, 0, true);
|
|
240
|
+
pos += 2;
|
|
241
|
+
view.setUint16(pos, 0, true);
|
|
242
|
+
pos += 2;
|
|
243
|
+
view.setUint16(pos, 0, true);
|
|
244
|
+
pos += 2;
|
|
245
|
+
view.setUint16(pos, 0, true);
|
|
246
|
+
pos += 2;
|
|
247
|
+
view.setUint32(pos, 0, true);
|
|
248
|
+
pos += 4;
|
|
249
|
+
view.setUint32(pos, entry.offset, true);
|
|
250
|
+
pos += 4;
|
|
251
|
+
bytes.set(entry.name, pos);
|
|
252
|
+
pos += entry.name.length;
|
|
253
|
+
}
|
|
254
|
+
view.setUint32(pos, 101010256, true);
|
|
255
|
+
pos += 4;
|
|
256
|
+
view.setUint16(pos, 0, true);
|
|
257
|
+
pos += 2;
|
|
258
|
+
view.setUint16(pos, 0, true);
|
|
259
|
+
pos += 2;
|
|
260
|
+
view.setUint16(pos, fileEntries.length, true);
|
|
261
|
+
pos += 2;
|
|
262
|
+
view.setUint16(pos, fileEntries.length, true);
|
|
263
|
+
pos += 2;
|
|
264
|
+
view.setUint32(pos, centralDirSize, true);
|
|
265
|
+
pos += 4;
|
|
266
|
+
view.setUint32(pos, centralDirectoryOffset, true);
|
|
267
|
+
pos += 4;
|
|
268
|
+
view.setUint16(pos, 0, true);
|
|
269
|
+
return new Blob([buffer], { type: "application/zip" });
|
|
331
270
|
}
|
|
332
|
-
/**
|
|
333
|
-
* Calculate CRC-32 for ZIP files
|
|
334
|
-
*/
|
|
335
271
|
function calculateCRC32(data) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
272
|
+
let crc = 4294967295;
|
|
273
|
+
const table = getCRC32Table();
|
|
274
|
+
for (let i = 0; i < data.length; i++) {
|
|
275
|
+
crc = crc >>> 8 ^ table[(crc ^ data[i]) & 255];
|
|
276
|
+
}
|
|
277
|
+
return (crc ^ 4294967295) >>> 0;
|
|
342
278
|
}
|
|
343
279
|
let crc32Table = null;
|
|
344
280
|
function getCRC32Table() {
|
|
345
|
-
|
|
346
|
-
return crc32Table;
|
|
347
|
-
}
|
|
348
|
-
crc32Table = new Uint32Array(256);
|
|
349
|
-
for (let i = 0; i < 256; i++) {
|
|
350
|
-
let c = i;
|
|
351
|
-
for (let j = 0; j < 8; j++) {
|
|
352
|
-
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
353
|
-
}
|
|
354
|
-
crc32Table[i] = c;
|
|
355
|
-
}
|
|
281
|
+
if (crc32Table) {
|
|
356
282
|
return crc32Table;
|
|
283
|
+
}
|
|
284
|
+
crc32Table = new Uint32Array(256);
|
|
285
|
+
for (let i = 0; i < 256; i++) {
|
|
286
|
+
let c = i;
|
|
287
|
+
for (let j = 0; j < 8; j++) {
|
|
288
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
289
|
+
}
|
|
290
|
+
crc32Table[i] = c;
|
|
291
|
+
}
|
|
292
|
+
return crc32Table;
|
|
357
293
|
}
|
|
358
|
-
/* ══════════════════════════════════════════════════════════════════════════════
|
|
359
|
-
Hook
|
|
360
|
-
══════════════════════════════════════════════════════════════════════════════ */
|
|
361
|
-
/**
|
|
362
|
-
* Hook for exporting desktop icons in multiple formats and sizes.
|
|
363
|
-
*
|
|
364
|
-
* @example
|
|
365
|
-
* ```tsx
|
|
366
|
-
* const { exportAll, downloadAll, icons, isExporting } = useDesktopIconExporter(MyIcon);
|
|
367
|
-
*
|
|
368
|
-
* // Export all icons
|
|
369
|
-
* await exportAll();
|
|
370
|
-
*
|
|
371
|
-
* // Download as ZIP
|
|
372
|
-
* await downloadAll();
|
|
373
|
-
* ```
|
|
374
|
-
*/
|
|
375
294
|
function useDesktopIconExporter(icon, initialConfig) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
catch (error) {
|
|
437
|
-
console.error('Failed to export icons:', error);
|
|
295
|
+
const [config, setConfig] = React.useState({
|
|
296
|
+
...DEFAULT_CONFIG,
|
|
297
|
+
...initialConfig
|
|
298
|
+
});
|
|
299
|
+
const [icons, setIcons] = React.useState([]);
|
|
300
|
+
const [isExporting, setIsExporting] = React.useState(false);
|
|
301
|
+
const [progress, setProgress] = React.useState(0);
|
|
302
|
+
const totalIcons = React.useMemo(() => {
|
|
303
|
+
let count = 0;
|
|
304
|
+
const scales = config.includeRetina ? RETINA_VARIANTS.length : 1;
|
|
305
|
+
for (const size of config.sizes) {
|
|
306
|
+
if (config.includeWindows || config.includeLinux) {
|
|
307
|
+
count += scales;
|
|
308
|
+
}
|
|
309
|
+
if (config.includeMacOS && size >= 128) {
|
|
310
|
+
count += scales;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return count;
|
|
314
|
+
}, [config]);
|
|
315
|
+
const exportAll = React.useCallback(async () => {
|
|
316
|
+
setIsExporting(true);
|
|
317
|
+
setProgress(0);
|
|
318
|
+
const exported = [];
|
|
319
|
+
let completed = 0;
|
|
320
|
+
try {
|
|
321
|
+
const scales = config.includeRetina ? RETINA_VARIANTS : [RETINA_VARIANTS[0]];
|
|
322
|
+
for (const size of config.sizes) {
|
|
323
|
+
for (const variant of scales) {
|
|
324
|
+
const actualSize = size * variant.scale;
|
|
325
|
+
const canvas = await renderIconToCanvas(
|
|
326
|
+
icon,
|
|
327
|
+
actualSize,
|
|
328
|
+
config.fillColor,
|
|
329
|
+
config.backgroundColor,
|
|
330
|
+
config.padding
|
|
331
|
+
);
|
|
332
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
333
|
+
const blob = await canvasToBlob(canvas);
|
|
334
|
+
const platforms = [];
|
|
335
|
+
if (config.includeWindows && size <= 256) {
|
|
336
|
+
platforms.push("windows");
|
|
337
|
+
}
|
|
338
|
+
if (config.includeMacOS && size >= 128) {
|
|
339
|
+
platforms.push("macos");
|
|
340
|
+
}
|
|
341
|
+
if (config.includeLinux) {
|
|
342
|
+
platforms.push("linux");
|
|
343
|
+
}
|
|
344
|
+
exported.push({
|
|
345
|
+
filename: `${config.name}-${size}x${size}${variant.suffix}.png`,
|
|
346
|
+
size,
|
|
347
|
+
scale: variant.scale,
|
|
348
|
+
platform: platforms.join(","),
|
|
349
|
+
dataUrl,
|
|
350
|
+
blob
|
|
351
|
+
});
|
|
352
|
+
completed++;
|
|
353
|
+
setProgress(completed / totalIcons * 100);
|
|
438
354
|
}
|
|
439
|
-
|
|
440
|
-
|
|
355
|
+
}
|
|
356
|
+
setIcons(exported);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error("Failed to export icons:", error);
|
|
359
|
+
} finally {
|
|
360
|
+
setIsExporting(false);
|
|
361
|
+
}
|
|
362
|
+
}, [icon, config, totalIcons]);
|
|
363
|
+
const exportWindows = React.useCallback(async () => {
|
|
364
|
+
const windowsSizes = config.sizes.filter((s) => s <= 256);
|
|
365
|
+
const pngBuffers = [];
|
|
366
|
+
for (const size of windowsSizes) {
|
|
367
|
+
const canvas = await renderIconToCanvas(
|
|
368
|
+
icon,
|
|
369
|
+
size,
|
|
370
|
+
config.fillColor,
|
|
371
|
+
config.backgroundColor,
|
|
372
|
+
config.padding
|
|
373
|
+
);
|
|
374
|
+
const blob = await canvasToBlob(canvas);
|
|
375
|
+
const buffer = await blob.arrayBuffer();
|
|
376
|
+
pngBuffers.push({ size, data: buffer });
|
|
377
|
+
}
|
|
378
|
+
const icoBlob = await createIcoFile(pngBuffers);
|
|
379
|
+
return URL.createObjectURL(icoBlob);
|
|
380
|
+
}, [icon, config]);
|
|
381
|
+
const exportMacOS = React.useCallback(async () => {
|
|
382
|
+
const macSizes = config.sizes.filter((s) => s >= 128);
|
|
383
|
+
const files = [];
|
|
384
|
+
for (const size of macSizes) {
|
|
385
|
+
const scales = config.includeRetina ? RETINA_VARIANTS : [RETINA_VARIANTS[0]];
|
|
386
|
+
for (const variant of scales) {
|
|
387
|
+
const actualSize = size * variant.scale;
|
|
388
|
+
const canvas = await renderIconToCanvas(
|
|
389
|
+
icon,
|
|
390
|
+
actualSize,
|
|
391
|
+
config.fillColor,
|
|
392
|
+
config.backgroundColor,
|
|
393
|
+
config.padding
|
|
394
|
+
);
|
|
395
|
+
const blob = await canvasToBlob(canvas);
|
|
396
|
+
files.push({
|
|
397
|
+
name: `${config.name}.iconset/icon_${size}x${size}${variant.suffix}.png`,
|
|
398
|
+
data: blob
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
files.push({
|
|
403
|
+
name: "README.txt",
|
|
404
|
+
data: new Blob(
|
|
405
|
+
[
|
|
406
|
+
`macOS Icon Set
|
|
407
|
+
=============
|
|
408
|
+
|
|
409
|
+
To convert to .icns format, use the following command in Terminal:
|
|
410
|
+
|
|
411
|
+
iconutil -c icns ${config.name}.iconset
|
|
412
|
+
|
|
413
|
+
This will create ${config.name}.icns
|
|
414
|
+
|
|
415
|
+
Note: iconutil is only available on macOS.
|
|
416
|
+
`
|
|
417
|
+
],
|
|
418
|
+
{ type: "text/plain" }
|
|
419
|
+
)
|
|
420
|
+
});
|
|
421
|
+
const zipBlob = await createZipFile(files);
|
|
422
|
+
return URL.createObjectURL(zipBlob);
|
|
423
|
+
}, [icon, config]);
|
|
424
|
+
const exportLinux = React.useCallback(async () => {
|
|
425
|
+
const linuxIcons = [];
|
|
426
|
+
for (const { size, dir } of LINUX_HICOLOR_DIRS) {
|
|
427
|
+
if (!config.sizes.includes(size)) {
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
const canvas = await renderIconToCanvas(
|
|
431
|
+
icon,
|
|
432
|
+
size,
|
|
433
|
+
config.fillColor,
|
|
434
|
+
config.backgroundColor,
|
|
435
|
+
config.padding
|
|
436
|
+
);
|
|
437
|
+
const dataUrl = canvas.toDataURL("image/png");
|
|
438
|
+
const blob = await canvasToBlob(canvas);
|
|
439
|
+
linuxIcons.push({
|
|
440
|
+
filename: `${dir}/${config.name}.png`,
|
|
441
|
+
size,
|
|
442
|
+
scale: 1,
|
|
443
|
+
platform: "linux",
|
|
444
|
+
dataUrl,
|
|
445
|
+
blob
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
return linuxIcons;
|
|
449
|
+
}, [icon, config]);
|
|
450
|
+
const downloadSingle = React.useCallback((exportedIcon) => {
|
|
451
|
+
const link = document.createElement("a");
|
|
452
|
+
link.href = exportedIcon.dataUrl;
|
|
453
|
+
link.download = exportedIcon.filename;
|
|
454
|
+
link.click();
|
|
455
|
+
}, []);
|
|
456
|
+
const downloadAll = React.useCallback(async () => {
|
|
457
|
+
setIsExporting(true);
|
|
458
|
+
setProgress(0);
|
|
459
|
+
try {
|
|
460
|
+
const files = [];
|
|
461
|
+
await exportAll();
|
|
462
|
+
for (const exportedIcon of icons) {
|
|
463
|
+
if (exportedIcon.blob) {
|
|
464
|
+
files.push({ name: `png/${exportedIcon.filename}`, data: exportedIcon.blob });
|
|
441
465
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const exportWindows = React.useCallback(async () => {
|
|
466
|
+
}
|
|
467
|
+
if (config.includeWindows) {
|
|
445
468
|
const windowsSizes = config.sizes.filter((s) => s <= 256);
|
|
446
469
|
const pngBuffers = [];
|
|
447
470
|
for (const size of windowsSizes) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
471
|
+
const canvas = await renderIconToCanvas(
|
|
472
|
+
icon,
|
|
473
|
+
size,
|
|
474
|
+
config.fillColor,
|
|
475
|
+
config.backgroundColor,
|
|
476
|
+
config.padding
|
|
477
|
+
);
|
|
478
|
+
const blob = await canvasToBlob(canvas);
|
|
479
|
+
pngBuffers.push({ size, data: await blob.arrayBuffer() });
|
|
452
480
|
}
|
|
453
481
|
const icoBlob = await createIcoFile(pngBuffers);
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const exportMacOS = React.useCallback(async () => {
|
|
482
|
+
files.push({ name: `windows/${config.name}.ico`, data: icoBlob });
|
|
483
|
+
}
|
|
484
|
+
if (config.includeMacOS) {
|
|
458
485
|
const macSizes = config.sizes.filter((s) => s >= 128);
|
|
459
|
-
const
|
|
486
|
+
const scales = config.includeRetina ? RETINA_VARIANTS : [RETINA_VARIANTS[0]];
|
|
460
487
|
for (const size of macSizes) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
488
|
+
for (const variant of scales) {
|
|
489
|
+
const actualSize = size * variant.scale;
|
|
490
|
+
const canvas = await renderIconToCanvas(
|
|
491
|
+
icon,
|
|
492
|
+
actualSize,
|
|
493
|
+
config.fillColor,
|
|
494
|
+
config.backgroundColor,
|
|
495
|
+
config.padding
|
|
496
|
+
);
|
|
497
|
+
const blob = await canvasToBlob(canvas);
|
|
498
|
+
files.push({
|
|
499
|
+
name: `macos/${config.name}.iconset/icon_${size}x${size}${variant.suffix}.png`,
|
|
500
|
+
data: blob
|
|
501
|
+
});
|
|
502
|
+
}
|
|
472
503
|
}
|
|
473
|
-
// Add README with iconutil instructions
|
|
474
504
|
files.push({
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
`macOS Icon Set
|
|
478
|
-
=============
|
|
479
|
-
|
|
480
|
-
To convert to .icns format, use the following command in Terminal:
|
|
481
|
-
|
|
482
|
-
iconutil -c icns ${config.name}.iconset
|
|
483
|
-
|
|
484
|
-
This will create ${config.name}.icns
|
|
485
|
-
|
|
486
|
-
Note: iconutil is only available on macOS.
|
|
487
|
-
`,
|
|
488
|
-
], { type: 'text/plain' }),
|
|
505
|
+
name: "macos/README.txt",
|
|
506
|
+
data: `To convert to .icns: iconutil -c icns ${config.name}.iconset`
|
|
489
507
|
});
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}, [icon, config]);
|
|
493
|
-
// Export Linux PNG set
|
|
494
|
-
const exportLinux = React.useCallback(async () => {
|
|
495
|
-
const linuxIcons = [];
|
|
508
|
+
}
|
|
509
|
+
if (config.includeLinux) {
|
|
496
510
|
for (const { size, dir } of LINUX_HICOLOR_DIRS) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
blob,
|
|
510
|
-
});
|
|
511
|
+
if (!config.sizes.includes(size)) {
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
const canvas = await renderIconToCanvas(
|
|
515
|
+
icon,
|
|
516
|
+
size,
|
|
517
|
+
config.fillColor,
|
|
518
|
+
config.backgroundColor,
|
|
519
|
+
config.padding
|
|
520
|
+
);
|
|
521
|
+
const blob = await canvasToBlob(canvas);
|
|
522
|
+
files.push({ name: `linux/${dir}/${config.name}.png`, data: blob });
|
|
511
523
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
524
|
+
}
|
|
525
|
+
const zipBlob = await createZipFile(files);
|
|
526
|
+
const url = URL.createObjectURL(zipBlob);
|
|
527
|
+
const link = document.createElement("a");
|
|
528
|
+
link.href = url;
|
|
529
|
+
link.download = `${config.name}-icons.zip`;
|
|
530
|
+
link.click();
|
|
531
|
+
URL.revokeObjectURL(url);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error("Failed to download icons:", error);
|
|
534
|
+
} finally {
|
|
535
|
+
setIsExporting(false);
|
|
536
|
+
}
|
|
537
|
+
}, [icon, icons, config, exportAll]);
|
|
538
|
+
return {
|
|
539
|
+
config,
|
|
540
|
+
setConfig,
|
|
541
|
+
icons,
|
|
542
|
+
isExporting,
|
|
543
|
+
progress,
|
|
544
|
+
exportAll,
|
|
545
|
+
exportWindows,
|
|
546
|
+
exportMacOS,
|
|
547
|
+
exportLinux,
|
|
548
|
+
downloadAll,
|
|
549
|
+
downloadSingle
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const NiceDesktopIconExporter = ({
|
|
553
|
+
icon,
|
|
554
|
+
initialConfig,
|
|
555
|
+
onExport,
|
|
556
|
+
className = ""
|
|
557
|
+
}) => {
|
|
558
|
+
const { t } = uiCore.useNiceTranslation();
|
|
559
|
+
const {
|
|
560
|
+
config,
|
|
561
|
+
setConfig,
|
|
562
|
+
icons,
|
|
563
|
+
isExporting,
|
|
564
|
+
progress,
|
|
565
|
+
exportAll,
|
|
566
|
+
downloadAll,
|
|
567
|
+
downloadSingle
|
|
568
|
+
} = useDesktopIconExporter(icon, initialConfig);
|
|
569
|
+
const handleExport = async () => {
|
|
570
|
+
await exportAll();
|
|
571
|
+
onExport?.(icons);
|
|
572
|
+
};
|
|
573
|
+
const IconComponent = icon;
|
|
574
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `nice-desktop-icon-exporter ${className}`, children: [
|
|
575
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__preview", children: [
|
|
576
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: t("desktopIconExporter.preview", "Preview") }),
|
|
577
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "nice-desktop-icon-exporter__preview-grid", children: [16, 32, 48, 64, 128, 256].map((size) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__preview-item", children: [
|
|
578
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
579
|
+
"div",
|
|
580
|
+
{
|
|
581
|
+
className: "nice-desktop-icon-exporter__preview-box",
|
|
582
|
+
style: {
|
|
583
|
+
width: Math.min(size, 128),
|
|
584
|
+
height: Math.min(size, 128),
|
|
585
|
+
backgroundColor: config.backgroundColor || "#f0f0f0"
|
|
586
|
+
},
|
|
587
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
588
|
+
IconComponent,
|
|
589
|
+
{
|
|
590
|
+
size: Math.min(size, 128) * (1 - config.padding * 2),
|
|
591
|
+
fill: config.fillColor
|
|
592
|
+
}
|
|
593
|
+
)
|
|
594
|
+
}
|
|
595
|
+
),
|
|
596
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
597
|
+
size,
|
|
598
|
+
"×",
|
|
599
|
+
size
|
|
600
|
+
] })
|
|
601
|
+
] }, size)) })
|
|
602
|
+
] }),
|
|
603
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__config", children: [
|
|
604
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: t("desktopIconExporter.configuration", "Configuration") }),
|
|
605
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
606
|
+
t("desktopIconExporter.iconName", "Icon Name:"),
|
|
607
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
608
|
+
"input",
|
|
609
|
+
{
|
|
610
|
+
type: "text",
|
|
611
|
+
value: config.name,
|
|
612
|
+
onChange: (e) => setConfig((c) => ({ ...c, name: e.target.value }))
|
|
613
|
+
}
|
|
614
|
+
)
|
|
615
|
+
] }),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
617
|
+
t("desktopIconExporter.fillColor", "Fill Color:"),
|
|
618
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
619
|
+
"input",
|
|
620
|
+
{
|
|
621
|
+
type: "color",
|
|
622
|
+
value: config.fillColor,
|
|
623
|
+
onChange: (e) => setConfig((c) => ({ ...c, fillColor: e.target.value }))
|
|
624
|
+
}
|
|
625
|
+
)
|
|
626
|
+
] }),
|
|
627
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
628
|
+
t("desktopIconExporter.background", "Background:"),
|
|
629
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
630
|
+
"input",
|
|
631
|
+
{
|
|
632
|
+
type: "color",
|
|
633
|
+
value: config.backgroundColor || "#ffffff",
|
|
634
|
+
onChange: (e) => setConfig((c) => ({ ...c, backgroundColor: e.target.value }))
|
|
635
|
+
}
|
|
636
|
+
),
|
|
637
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setConfig((c) => ({ ...c, backgroundColor: void 0 })), children: t("desktopIconExporter.transparent", "Transparent") })
|
|
638
|
+
] }),
|
|
639
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
640
|
+
t("desktopIconExporter.padding", "Padding: {padding}%").replace(
|
|
641
|
+
"{padding}",
|
|
642
|
+
String(Math.round(config.padding * 100))
|
|
643
|
+
),
|
|
644
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
645
|
+
"input",
|
|
646
|
+
{
|
|
647
|
+
type: "range",
|
|
648
|
+
min: "0",
|
|
649
|
+
max: "0.3",
|
|
650
|
+
step: "0.01",
|
|
651
|
+
value: config.padding,
|
|
652
|
+
onChange: (e) => setConfig((c) => ({ ...c, padding: parseFloat(e.target.value) }))
|
|
653
|
+
}
|
|
654
|
+
)
|
|
655
|
+
] }),
|
|
656
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__platforms", children: [
|
|
657
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
658
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
659
|
+
"input",
|
|
660
|
+
{
|
|
661
|
+
type: "checkbox",
|
|
662
|
+
checked: config.includeWindows,
|
|
663
|
+
onChange: (e) => setConfig((c) => ({ ...c, includeWindows: e.target.checked }))
|
|
534
664
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
files.push({ name: `windows/${config.name}.ico`, data: icoBlob });
|
|
665
|
+
),
|
|
666
|
+
t("desktopIconExporter.windowsIco", "Windows (.ico)")
|
|
667
|
+
] }),
|
|
668
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
669
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
670
|
+
"input",
|
|
671
|
+
{
|
|
672
|
+
type: "checkbox",
|
|
673
|
+
checked: config.includeMacOS,
|
|
674
|
+
onChange: (e) => setConfig((c) => ({ ...c, includeMacOS: e.target.checked }))
|
|
546
675
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
name: `macos/${config.name}.iconset/icon_${size}x${size}${variant.suffix}.png`,
|
|
558
|
-
data: blob,
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
files.push({
|
|
563
|
-
name: 'macos/README.txt',
|
|
564
|
-
data: `To convert to .icns: iconutil -c icns ${config.name}.iconset`,
|
|
565
|
-
});
|
|
676
|
+
),
|
|
677
|
+
t("desktopIconExporter.macosIconset", "macOS (.iconset)")
|
|
678
|
+
] }),
|
|
679
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
680
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
681
|
+
"input",
|
|
682
|
+
{
|
|
683
|
+
type: "checkbox",
|
|
684
|
+
checked: config.includeLinux,
|
|
685
|
+
onChange: (e) => setConfig((c) => ({ ...c, includeLinux: e.target.checked }))
|
|
566
686
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
687
|
+
),
|
|
688
|
+
t("desktopIconExporter.linuxHicolor", "Linux (hicolor)")
|
|
689
|
+
] }),
|
|
690
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
691
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
692
|
+
"input",
|
|
693
|
+
{
|
|
694
|
+
type: "checkbox",
|
|
695
|
+
checked: config.includeRetina,
|
|
696
|
+
onChange: (e) => setConfig((c) => ({ ...c, includeRetina: e.target.checked }))
|
|
577
697
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
* <NiceDesktopIconExporter
|
|
622
|
-
* icon={NtdRocket}
|
|
623
|
-
* initialConfig={{ name: 'my-app', fillColor: '#ff6b35' }}
|
|
624
|
-
* />
|
|
625
|
-
* );
|
|
626
|
-
* }
|
|
627
|
-
* ```
|
|
628
|
-
*/
|
|
629
|
-
const NiceDesktopIconExporter = ({ icon, initialConfig, onExport, className = '', }) => {
|
|
630
|
-
const { t } = uiCore.useNiceTranslation();
|
|
631
|
-
const { config, setConfig, icons, isExporting, progress, exportAll, downloadAll, downloadSingle, } = useDesktopIconExporter(icon, initialConfig);
|
|
632
|
-
const handleExport = async () => {
|
|
633
|
-
await exportAll();
|
|
634
|
-
onExport === null || onExport === void 0 ? void 0 : onExport(icons);
|
|
635
|
-
};
|
|
636
|
-
const IconComponent = icon;
|
|
637
|
-
return (jsxRuntime.jsxs("div", { className: `nice-desktop-icon-exporter ${className}`, children: [jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__preview", children: [jsxRuntime.jsx("h3", { children: t('desktopIconExporter.preview', 'Preview') }), jsxRuntime.jsx("div", { className: "nice-desktop-icon-exporter__preview-grid", children: [16, 32, 48, 64, 128, 256].map((size) => (jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__preview-item", children: [jsxRuntime.jsx("div", { className: "nice-desktop-icon-exporter__preview-box", style: {
|
|
638
|
-
width: Math.min(size, 128),
|
|
639
|
-
height: Math.min(size, 128),
|
|
640
|
-
backgroundColor: config.backgroundColor || '#f0f0f0',
|
|
641
|
-
}, children: jsxRuntime.jsx(IconComponent, { size: Math.min(size, 128) * (1 - config.padding * 2), fill: config.fillColor }) }), jsxRuntime.jsxs("span", { children: [size, "\u00D7", size] })] }, size))) })] }), jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__config", children: [jsxRuntime.jsx("h3", { children: t('desktopIconExporter.configuration', 'Configuration') }), jsxRuntime.jsxs("label", { children: [t('desktopIconExporter.iconName', 'Icon Name:'), jsxRuntime.jsx("input", { type: "text", value: config.name, onChange: (e) => setConfig((c) => ({ ...c, name: e.target.value })) })] }), jsxRuntime.jsxs("label", { children: [t('desktopIconExporter.fillColor', 'Fill Color:'), jsxRuntime.jsx("input", { type: "color", value: config.fillColor, onChange: (e) => setConfig((c) => ({ ...c, fillColor: e.target.value })) })] }), jsxRuntime.jsxs("label", { children: [t('desktopIconExporter.background', 'Background:'), jsxRuntime.jsx("input", { type: "color", value: config.backgroundColor || '#ffffff', onChange: (e) => setConfig((c) => ({ ...c, backgroundColor: e.target.value })) }), jsxRuntime.jsx("button", { onClick: () => setConfig((c) => ({ ...c, backgroundColor: undefined })), children: t('desktopIconExporter.transparent', 'Transparent') })] }), jsxRuntime.jsxs("label", { children: [t('desktopIconExporter.padding', 'Padding: {padding}%').replace('{padding}', String(Math.round(config.padding * 100))), jsxRuntime.jsx("input", { type: "range", min: "0", max: "0.3", step: "0.01", value: config.padding, onChange: (e) => setConfig((c) => ({ ...c, padding: parseFloat(e.target.value) })) })] }), jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__platforms", children: [jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.includeWindows, onChange: (e) => setConfig((c) => ({ ...c, includeWindows: e.target.checked })) }), t('desktopIconExporter.windowsIco', 'Windows (.ico)')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.includeMacOS, onChange: (e) => setConfig((c) => ({ ...c, includeMacOS: e.target.checked })) }), t('desktopIconExporter.macosIconset', 'macOS (.iconset)')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.includeLinux, onChange: (e) => setConfig((c) => ({ ...c, includeLinux: e.target.checked })) }), t('desktopIconExporter.linuxHicolor', 'Linux (hicolor)')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.includeRetina, onChange: (e) => setConfig((c) => ({ ...c, includeRetina: e.target.checked })) }), t('desktopIconExporter.hidpiRetina2x3x', 'HiDPI/Retina (@2x, @3x)')] })] }), jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__sizes", children: [jsxRuntime.jsx("h4", { children: t('desktopIconExporter.sizes', 'Sizes:') }), DESKTOP_ICON_SIZES.map(({ size, label }) => (jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.sizes.includes(size), onChange: (e) => {
|
|
642
|
-
setConfig((c) => ({
|
|
643
|
-
...c,
|
|
644
|
-
sizes: e.target.checked
|
|
645
|
-
? [...c.sizes, size].sort((a, b) => a - b)
|
|
646
|
-
: c.sizes.filter((s) => s !== size),
|
|
647
|
-
}));
|
|
648
|
-
} }), label] }, size)))] })] }), jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__actions", children: [jsxRuntime.jsx("button", { onClick: handleExport, disabled: isExporting, children: isExporting
|
|
649
|
-
? t('desktopIconExporter.exporting', 'Exporting... {progress}%').replace('{progress}', String(Math.round(progress)))
|
|
650
|
-
: t('desktopIconExporter.generateIcons', 'Generate Icons') }), jsxRuntime.jsx("button", { onClick: downloadAll, disabled: isExporting || icons.length === 0, children: t('desktopIconExporter.downloadAllZip', 'Download All (ZIP)') })] }), icons.length > 0 && (jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__results", children: [jsxRuntime.jsx("h3", { children: t('desktopIconExporter.generatedIcons', 'Generated Icons ({icons})').replace('{icons}', String(icons.length)) }), jsxRuntime.jsx("div", { className: "nice-desktop-icon-exporter__results-grid", children: icons.map((exportedIcon) => (jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__result-item", children: [jsxRuntime.jsx("img", { src: exportedIcon.dataUrl, alt: exportedIcon.filename }), jsxRuntime.jsx("span", { children: exportedIcon.filename }), jsxRuntime.jsx("button", { onClick: () => downloadSingle(exportedIcon), children: t('desktopIconExporter.download', 'Download') })] }, exportedIcon.filename))) })] }))] }));
|
|
698
|
+
),
|
|
699
|
+
t("desktopIconExporter.hidpiRetina2x3x", "HiDPI/Retina (@2x, @3x)")
|
|
700
|
+
] })
|
|
701
|
+
] }),
|
|
702
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__sizes", children: [
|
|
703
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { children: t("desktopIconExporter.sizes", "Sizes:") }),
|
|
704
|
+
DESKTOP_ICON_SIZES.map(({ size, label }) => /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
|
|
705
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
706
|
+
"input",
|
|
707
|
+
{
|
|
708
|
+
type: "checkbox",
|
|
709
|
+
checked: config.sizes.includes(size),
|
|
710
|
+
onChange: (e) => {
|
|
711
|
+
setConfig((c) => ({
|
|
712
|
+
...c,
|
|
713
|
+
sizes: e.target.checked ? [...c.sizes, size].sort((a, b) => a - b) : c.sizes.filter((s) => s !== size)
|
|
714
|
+
}));
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
),
|
|
718
|
+
label
|
|
719
|
+
] }, size))
|
|
720
|
+
] })
|
|
721
|
+
] }),
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__actions", children: [
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleExport, disabled: isExporting, children: isExporting ? t("desktopIconExporter.exporting", "Exporting... {progress}%").replace(
|
|
724
|
+
"{progress}",
|
|
725
|
+
String(Math.round(progress))
|
|
726
|
+
) : t("desktopIconExporter.generateIcons", "Generate Icons") }),
|
|
727
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: downloadAll, disabled: isExporting || icons.length === 0, children: t("desktopIconExporter.downloadAllZip", "Download All (ZIP)") })
|
|
728
|
+
] }),
|
|
729
|
+
icons.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__results", children: [
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { children: t("desktopIconExporter.generatedIcons", "Generated Icons ({icons})").replace(
|
|
731
|
+
"{icons}",
|
|
732
|
+
String(icons.length)
|
|
733
|
+
) }),
|
|
734
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "nice-desktop-icon-exporter__results-grid", children: icons.map((exportedIcon) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nice-desktop-icon-exporter__result-item", children: [
|
|
735
|
+
/* @__PURE__ */ jsxRuntime.jsx("img", { src: exportedIcon.dataUrl, alt: exportedIcon.filename }),
|
|
736
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: exportedIcon.filename }),
|
|
737
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => downloadSingle(exportedIcon), children: t("desktopIconExporter.download", "Download") })
|
|
738
|
+
] }, exportedIcon.filename)) })
|
|
739
|
+
] })
|
|
740
|
+
] });
|
|
651
741
|
};
|
|
652
|
-
|
|
653
742
|
exports.DESKTOP_ICON_SIZES = DESKTOP_ICON_SIZES;
|
|
654
743
|
exports.LINUX_HICOLOR_DIRS = LINUX_HICOLOR_DIRS;
|
|
655
744
|
exports.NiceDesktopIconExporter = NiceDesktopIconExporter;
|