@spark-apps/piclet 1.0.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/LICENSE +21 -0
- package/Readme.md +180 -0
- package/dist/cli.js +2989 -0
- package/dist/cli.js.map +1 -0
- package/dist/gui/css/theme.css +88 -0
- package/dist/gui/iconpack.html +113 -0
- package/dist/gui/img/logo.ico +0 -0
- package/dist/gui/js/piclet.js +75 -0
- package/dist/gui/loading.hta +96 -0
- package/dist/gui/makeicon.html +165 -0
- package/dist/gui/piclet.html +998 -0
- package/dist/gui/remove-bg.html +178 -0
- package/dist/gui/rescale.html +195 -0
- package/dist/gui/storepack.html +179 -0
- package/dist/icons/banana.ico +0 -0
- package/dist/icons/iconpack.ico +0 -0
- package/dist/icons/makeicon.ico +0 -0
- package/dist/icons/removebg.ico +0 -0
- package/dist/icons/rescale.ico +0 -0
- package/dist/icons/storepack.ico +0 -0
- package/dist/launcher.vbs +44 -0
- package/package.json +72 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html data-piclet data-width="340" data-height="310">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Icon Pack</title>
|
|
7
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
8
|
+
<script src="/js/piclet.js"></script>
|
|
9
|
+
<style>
|
|
10
|
+
.platforms{display:flex;flex-direction:column;gap:4px;flex:1}
|
|
11
|
+
.platforms .opt{padding:8px 10px;background:var(--bg2);border-radius:6px;border:1px solid var(--brd)}
|
|
12
|
+
.platforms .opt:hover{border-color:var(--acc)}
|
|
13
|
+
.platform-info{font-size:10px;color:var(--txt3);margin-left:22px;margin-top:2px}
|
|
14
|
+
</style>
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<div class="app">
|
|
18
|
+
<div class="hd"><b>PicLet</b><span>Icon Pack Generator</span></div>
|
|
19
|
+
<div class="meta">
|
|
20
|
+
<div>File<b id="fn">-</b></div>
|
|
21
|
+
<div>Size<b id="sz">-</b></div>
|
|
22
|
+
</div>
|
|
23
|
+
<!-- Form state -->
|
|
24
|
+
<div id="F" class="form">
|
|
25
|
+
<div class="platforms">
|
|
26
|
+
<label class="opt">
|
|
27
|
+
<input type="checkbox" id="cW" checked>
|
|
28
|
+
<span class="box"><svg viewBox="0 0 12 12"><polyline points="2,6 5,9 10,3"/></svg></span>
|
|
29
|
+
Web
|
|
30
|
+
<div class="platform-info">favicon, PWA, apple-touch-icon</div>
|
|
31
|
+
</label>
|
|
32
|
+
<label class="opt">
|
|
33
|
+
<input type="checkbox" id="cA" checked>
|
|
34
|
+
<span class="box"><svg viewBox="0 0 12 12"><polyline points="2,6 5,9 10,3"/></svg></span>
|
|
35
|
+
Android
|
|
36
|
+
<div class="platform-info">mipmap folders, Play Store icon</div>
|
|
37
|
+
</label>
|
|
38
|
+
<label class="opt">
|
|
39
|
+
<input type="checkbox" id="cI" checked>
|
|
40
|
+
<span class="box"><svg viewBox="0 0 12 12"><polyline points="2,6 5,9 10,3"/></svg></span>
|
|
41
|
+
iOS
|
|
42
|
+
<div class="platform-info">App icons, App Store icon</div>
|
|
43
|
+
</label>
|
|
44
|
+
</div>
|
|
45
|
+
<div class="warn-box" id="wB"></div>
|
|
46
|
+
<div class="btns">
|
|
47
|
+
<button class="btn btn-g" onclick="PicLet.close()">Cancel</button>
|
|
48
|
+
<button class="btn btn-p" onclick="generate()">Generate</button>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<!-- Loading state -->
|
|
52
|
+
<div class="ld" id="L"><div class="sp"></div><span id="lT">Generating icons...</span></div>
|
|
53
|
+
<!-- Log -->
|
|
54
|
+
<div class="log" id="G"></div>
|
|
55
|
+
<!-- Done state -->
|
|
56
|
+
<div class="dn" id="D">
|
|
57
|
+
<h4 id="dT"></h4>
|
|
58
|
+
<p id="dM"></p>
|
|
59
|
+
<div class="btns" style="width:100%;margin-top:8px">
|
|
60
|
+
<button class="btn btn-p" onclick="PicLet.close()">Done</button>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<script>
|
|
65
|
+
const { $, log, fetchJson, postJson } = PicLet;
|
|
66
|
+
|
|
67
|
+
// Load initial data and check for warnings
|
|
68
|
+
fetchJson('/api/info').then(d => {
|
|
69
|
+
$('fn').textContent = d.fileName;
|
|
70
|
+
$('sz').textContent = d.width + '×' + d.height;
|
|
71
|
+
if (d.width < 1024 || d.height < 1024) {
|
|
72
|
+
$('wB').textContent = 'Source < 1024px. Larger images produce better quality.';
|
|
73
|
+
$('wB').classList.add('on');
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Generate icons
|
|
78
|
+
async function generate() {
|
|
79
|
+
const web = $('cW').checked;
|
|
80
|
+
const android = $('cA').checked;
|
|
81
|
+
const ios = $('cI').checked;
|
|
82
|
+
|
|
83
|
+
if (!web && !android && !ios) {
|
|
84
|
+
alert('Please select at least one platform');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
$('F').classList.add('hide');
|
|
89
|
+
$('L').classList.add('on');
|
|
90
|
+
$('G').classList.add('on');
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const result = await postJson('/api/process', { web, android, ios });
|
|
94
|
+
|
|
95
|
+
if (result.logs) {
|
|
96
|
+
result.logs.forEach(l => log('G', l.type[0], l.message));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
$('L').classList.remove('on');
|
|
100
|
+
$('D').classList.add('on', result.success ? 'ok' : 'err');
|
|
101
|
+
$('dT').textContent = result.success ? 'Done' : 'Failed';
|
|
102
|
+
$('dM').textContent = result.success ? result.output : result.error;
|
|
103
|
+
} catch (e) {
|
|
104
|
+
log('G', 'e', e.message);
|
|
105
|
+
$('L').classList.remove('on');
|
|
106
|
+
$('D').classList.add('on', 'err');
|
|
107
|
+
$('dT').textContent = 'Error';
|
|
108
|
+
$('dM').textContent = e.message;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
</script>
|
|
112
|
+
</body>
|
|
113
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PicLet Shared GUI Utilities
|
|
3
|
+
*/
|
|
4
|
+
(function() {
|
|
5
|
+
// DOM helper
|
|
6
|
+
function $(id) {
|
|
7
|
+
return document.getElementById(id);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Logging helper
|
|
11
|
+
function log(container, type, msg) {
|
|
12
|
+
const el = typeof container === 'string' ? $(container) : container;
|
|
13
|
+
el.classList.add('on');
|
|
14
|
+
el.innerHTML += `<p class="${type}">${msg}</p>`;
|
|
15
|
+
el.scrollTop = el.scrollHeight;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Fetch JSON helper
|
|
19
|
+
async function fetchJson(url, opts) {
|
|
20
|
+
const res = await fetch(url, opts);
|
|
21
|
+
const text = await res.text();
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(text);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
console.error('Invalid JSON response:', text.substring(0, 200));
|
|
26
|
+
throw new Error('Server returned invalid response');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// POST JSON helper
|
|
31
|
+
async function postJson(url, data) {
|
|
32
|
+
return fetchJson(url, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify(data)
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Close window via API
|
|
40
|
+
function close() {
|
|
41
|
+
fetch('/api/close', { method: 'POST' }).finally(() => window.close());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Initialize window
|
|
45
|
+
function init(opts = {}) {
|
|
46
|
+
const w = opts.width || 320;
|
|
47
|
+
const h = opts.height || 280;
|
|
48
|
+
|
|
49
|
+
// Center on available screen (excludes taskbar)
|
|
50
|
+
const x = Math.round((screen.availWidth - w) / 2) + (screen.availLeft || 0);
|
|
51
|
+
const y = Math.round((screen.availHeight - h) / 2) + (screen.availTop || 0);
|
|
52
|
+
window.moveTo(x, y);
|
|
53
|
+
window.resizeTo(w, h);
|
|
54
|
+
|
|
55
|
+
// Reset zoom
|
|
56
|
+
document.documentElement.style.zoom = '100%';
|
|
57
|
+
|
|
58
|
+
// Disable context menu
|
|
59
|
+
document.addEventListener('contextmenu', e => e.preventDefault());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Export to global
|
|
63
|
+
window.PicLet = { $, log, fetchJson, postJson, close, init };
|
|
64
|
+
|
|
65
|
+
// Auto-init on DOMContentLoaded if data attributes present
|
|
66
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
67
|
+
const html = document.documentElement;
|
|
68
|
+
if (html.dataset.piclet !== undefined) {
|
|
69
|
+
init({
|
|
70
|
+
width: parseInt(html.dataset.width) || undefined,
|
|
71
|
+
height: parseInt(html.dataset.height) || undefined
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
})();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
4
|
+
<title>PicLet</title>
|
|
5
|
+
<hta:application
|
|
6
|
+
id="PicLetLoader"
|
|
7
|
+
applicationName="PicLet"
|
|
8
|
+
border="none"
|
|
9
|
+
borderStyle="none"
|
|
10
|
+
caption="no"
|
|
11
|
+
innerBorder="no"
|
|
12
|
+
maximizeButton="no"
|
|
13
|
+
minimizeButton="no"
|
|
14
|
+
showInTaskbar="no"
|
|
15
|
+
singleInstance="yes"
|
|
16
|
+
scroll="no"
|
|
17
|
+
contextMenu="no"
|
|
18
|
+
selection="no"
|
|
19
|
+
sysmenu="no"
|
|
20
|
+
/>
|
|
21
|
+
<style>
|
|
22
|
+
*{margin:0;padding:0}
|
|
23
|
+
html,body{height:100%;overflow:hidden;background:linear-gradient(180deg,#0a0a0c 0%,#111113 100%);transition:opacity .4s ease-out}
|
|
24
|
+
body.fade-out{opacity:0}
|
|
25
|
+
body{font-family:Segoe UI,sans-serif;color:#e4e4e7;text-align:center;padding:24px}
|
|
26
|
+
.logo{width:80px;height:80px;margin-bottom:8px}
|
|
27
|
+
.title{font-size:28px;font-weight:700;background:linear-gradient(135deg,#eab308 0%,#fbbf24 50%,#f59e0b 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:2px}
|
|
28
|
+
.tagline{font-size:10px;color:#fcd34d;font-weight:600;letter-spacing:1px;text-transform:uppercase;margin-bottom:4px}
|
|
29
|
+
.desc{font-size:10px;color:#636366;margin-bottom:14px}
|
|
30
|
+
.loading{font-size:12px;color:#636366;margin-bottom:6px}
|
|
31
|
+
.dots{margin-bottom:20px}
|
|
32
|
+
.dot{display:inline-block;width:6px;height:6px;background:#eab308;border-radius:50%;margin:0 3px}
|
|
33
|
+
.divider{width:80%;height:1px;background:#222;margin:0 auto 14px auto}
|
|
34
|
+
.website{display:inline-block;font-size:11px;color:#636366;text-decoration:none;padding:6px 14px;background:#111113;border:1px solid #222;border-radius:5px;margin-bottom:12px;transition:all .2s}
|
|
35
|
+
.website:hover{color:#eab308;border-color:#eab308;background:rgba(234,179,8,0.05)}
|
|
36
|
+
.promo{font-size:9px;color:#4a4a4f;margin-bottom:4px}
|
|
37
|
+
.brand{font-size:11px;color:#636366;font-weight:500;text-decoration:none}
|
|
38
|
+
.brand:hover{color:#fcd34d}
|
|
39
|
+
.sub{font-size:9px;color:#4a4a4f;margin-top:2px}
|
|
40
|
+
</style>
|
|
41
|
+
<script language="javascript">
|
|
42
|
+
var w = 280, h = 385;
|
|
43
|
+
window.resizeTo(w, h);
|
|
44
|
+
window.moveTo((screen.availWidth - w) / 2, (screen.availHeight - h) / 2);
|
|
45
|
+
|
|
46
|
+
// Failsafe timeout
|
|
47
|
+
setTimeout(function() { window.close(); }, 10000);
|
|
48
|
+
|
|
49
|
+
// Poll for ready signal file
|
|
50
|
+
var fso = new ActiveXObject("Scripting.FileSystemObject");
|
|
51
|
+
var shell = new ActiveXObject("WScript.Shell");
|
|
52
|
+
var readyFile = shell.ExpandEnvironmentStrings("%TEMP%") + "\\piclet-ready.tmp";
|
|
53
|
+
var closing = false;
|
|
54
|
+
setInterval(function() {
|
|
55
|
+
if (!closing && fso.FileExists(readyFile)) {
|
|
56
|
+
closing = true;
|
|
57
|
+
try { fso.DeleteFile(readyFile); } catch(e) {}
|
|
58
|
+
document.body.className = 'fade-out';
|
|
59
|
+
setTimeout(function() { window.close(); }, 400);
|
|
60
|
+
}
|
|
61
|
+
}, 200);
|
|
62
|
+
|
|
63
|
+
// Animate dots
|
|
64
|
+
var idx = 0;
|
|
65
|
+
setInterval(function() {
|
|
66
|
+
var d1 = document.getElementById('d1');
|
|
67
|
+
var d2 = document.getElementById('d2');
|
|
68
|
+
var d3 = document.getElementById('d3');
|
|
69
|
+
if(d1 && d2 && d3) {
|
|
70
|
+
d1.style.opacity = 0.3; d2.style.opacity = 0.3; d3.style.opacity = 0.3;
|
|
71
|
+
if (idx % 3 == 0) d1.style.opacity = 1;
|
|
72
|
+
if (idx % 3 == 1) d2.style.opacity = 1;
|
|
73
|
+
if (idx % 3 == 2) d3.style.opacity = 1;
|
|
74
|
+
idx++;
|
|
75
|
+
}
|
|
76
|
+
}, 300);
|
|
77
|
+
</script>
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<img class="logo" src="img/logo.ico">
|
|
81
|
+
<div class="title">PicLet</div>
|
|
82
|
+
<div class="tagline">Edit. Scale. Ship.</div>
|
|
83
|
+
<div class="desc">Lightweight image tools for content creators</div>
|
|
84
|
+
<div class="loading">Loading your tools...</div>
|
|
85
|
+
<div class="dots">
|
|
86
|
+
<div id="d1" class="dot"></div>
|
|
87
|
+
<div id="d2" class="dot"></div>
|
|
88
|
+
<div id="d3" class="dot"></div>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="divider"></div>
|
|
91
|
+
<a class="website" href="https://piclet.app" target="_blank">piclet.app</a>
|
|
92
|
+
<div class="promo">A free tool by</div>
|
|
93
|
+
<a class="brand" href="https://spark-games.co.uk" target="_blank">spark-games.co.uk</a>
|
|
94
|
+
<div class="sub">Game Dev Tools & Resources</div>
|
|
95
|
+
</body>
|
|
96
|
+
</html>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html data-piclet data-width="400" data-height="420">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Make Icon</title>
|
|
7
|
+
<link rel="stylesheet" href="/css/theme.css">
|
|
8
|
+
<script src="/js/piclet.js"></script>
|
|
9
|
+
<style>
|
|
10
|
+
.preview-area{flex:1;display:flex;align-items:center;justify-content:center;background:var(--bg2);border-radius:6px;min-height:140px;overflow:hidden;position:relative}
|
|
11
|
+
.preview-area::before{content:'';position:absolute;inset:0;background:repeating-conic-gradient(#1a1a1d 0% 25%, #222 0% 50%) 50%/16px 16px;z-index:0}
|
|
12
|
+
.preview-area img{max-width:100%;max-height:100%;object-fit:contain;position:relative;z-index:1}
|
|
13
|
+
.preview-area .placeholder{position:relative;z-index:1;font-size:11px;color:var(--txt3)}
|
|
14
|
+
.preview-area .mini-sp{width:16px;height:16px;border:2px solid var(--bg3);border-top-color:var(--acc);border-radius:50%;animation:s .5s linear infinite;position:relative;z-index:1}
|
|
15
|
+
.preview-info{font-size:10px;color:var(--txt3);text-align:center;margin-top:4px;height:14px}
|
|
16
|
+
.info-row{display:flex;justify-content:space-between;font-size:11px;padding:6px 10px;background:var(--bg2);border-radius:4px}
|
|
17
|
+
.info-row .label{color:var(--txt3)}
|
|
18
|
+
.info-row .value{color:var(--txt2)}
|
|
19
|
+
</style>
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<div class="app">
|
|
23
|
+
<div class="hd"><b>PicLet</b><span>Make Icon</span></div>
|
|
24
|
+
<div class="meta">
|
|
25
|
+
<div>File<b id="fn">-</b></div>
|
|
26
|
+
<div>Size<b id="sz">-</b></div>
|
|
27
|
+
</div>
|
|
28
|
+
<!-- Form state -->
|
|
29
|
+
<div id="F" class="form">
|
|
30
|
+
<div class="preview-area" id="pA">
|
|
31
|
+
<span class="placeholder">Loading preview...</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="preview-info" id="pI"></div>
|
|
34
|
+
<div class="info-row">
|
|
35
|
+
<span class="label">Output</span>
|
|
36
|
+
<span class="value" id="oN">-</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="opts">
|
|
39
|
+
<label class="opt" data-tip="Remove transparent edges around the image"><input type="checkbox" id="cT" checked><span class="box"><svg viewBox="0 0 12 12"><polyline points="2,6 5,9 10,3"/></svg></span>Auto-trim</label>
|
|
40
|
+
<label class="opt" data-tip="Add transparent padding to make a square icon"><input type="checkbox" id="cS" checked><span class="box"><svg viewBox="0 0 12 12"><polyline points="2,6 5,9 10,3"/></svg></span>Make square</label>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="btns">
|
|
43
|
+
<button class="btn btn-g" onclick="PicLet.close()">Cancel</button>
|
|
44
|
+
<button class="btn btn-p" onclick="convert()">Convert</button>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
<!-- Loading state -->
|
|
48
|
+
<div class="ld" id="L"><div class="sp"></div><span id="lT">Converting...</span></div>
|
|
49
|
+
<!-- Log -->
|
|
50
|
+
<div class="log" id="G"></div>
|
|
51
|
+
<!-- Done state -->
|
|
52
|
+
<div class="dn" id="D">
|
|
53
|
+
<h4 id="dT"></h4>
|
|
54
|
+
<p id="dM"></p>
|
|
55
|
+
<div class="btns" style="width:100%;margin-top:8px">
|
|
56
|
+
<button class="btn btn-p" onclick="PicLet.close()">Done</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<script>
|
|
61
|
+
const { $, log, fetchJson, postJson } = PicLet;
|
|
62
|
+
const pA = $('pA'), pI = $('pI');
|
|
63
|
+
|
|
64
|
+
let previewTimeout = null;
|
|
65
|
+
let lastPreviewOpts = null;
|
|
66
|
+
|
|
67
|
+
// Get current options
|
|
68
|
+
function getOptions() {
|
|
69
|
+
return {
|
|
70
|
+
trim: $('cT').checked,
|
|
71
|
+
makeSquare: $('cS').checked
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check if options changed
|
|
76
|
+
function optionsChanged() {
|
|
77
|
+
const current = JSON.stringify(getOptions());
|
|
78
|
+
if (current === lastPreviewOpts) return false;
|
|
79
|
+
lastPreviewOpts = current;
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Schedule preview (debounced)
|
|
84
|
+
function schedulePreview() {
|
|
85
|
+
if (previewTimeout) clearTimeout(previewTimeout);
|
|
86
|
+
previewTimeout = setTimeout(() => {
|
|
87
|
+
if (optionsChanged()) {
|
|
88
|
+
generatePreview();
|
|
89
|
+
}
|
|
90
|
+
}, 300);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Generate preview
|
|
94
|
+
async function generatePreview() {
|
|
95
|
+
pA.innerHTML = '<div class="mini-sp"></div>';
|
|
96
|
+
pI.textContent = '';
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const result = await postJson('/api/preview', getOptions());
|
|
100
|
+
|
|
101
|
+
if (result.success && result.imageData) {
|
|
102
|
+
const img = document.createElement('img');
|
|
103
|
+
img.src = result.imageData;
|
|
104
|
+
img.alt = 'Preview';
|
|
105
|
+
pA.innerHTML = '';
|
|
106
|
+
pA.appendChild(img);
|
|
107
|
+
pI.textContent = result.width && result.height ? `${result.width}×${result.height}` : '';
|
|
108
|
+
} else {
|
|
109
|
+
pA.innerHTML = '<span class="placeholder">' + (result.error || 'Preview failed') + '</span>';
|
|
110
|
+
pI.textContent = '';
|
|
111
|
+
}
|
|
112
|
+
} catch (e) {
|
|
113
|
+
pA.innerHTML = '<span class="placeholder">Preview error</span>';
|
|
114
|
+
pI.textContent = '';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Checkbox changes trigger preview
|
|
119
|
+
$('cT').onchange = schedulePreview;
|
|
120
|
+
$('cS').onchange = schedulePreview;
|
|
121
|
+
|
|
122
|
+
// Load initial data
|
|
123
|
+
fetchJson('/api/info').then(d => {
|
|
124
|
+
$('fn').textContent = d.fileName;
|
|
125
|
+
$('sz').textContent = d.width + '×' + d.height;
|
|
126
|
+
// Show output filename
|
|
127
|
+
const baseName = d.fileName.replace(/\.[^.]+$/, '');
|
|
128
|
+
$('oN').textContent = baseName + '.ico';
|
|
129
|
+
// Load defaults
|
|
130
|
+
if (d.defaults) {
|
|
131
|
+
$('cT').checked = d.defaults.trim ?? true;
|
|
132
|
+
$('cS').checked = d.defaults.makeSquare ?? true;
|
|
133
|
+
}
|
|
134
|
+
// Generate initial preview
|
|
135
|
+
setTimeout(generatePreview, 100);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Convert to ICO
|
|
139
|
+
async function convert() {
|
|
140
|
+
$('F').classList.add('hide');
|
|
141
|
+
$('L').classList.add('on');
|
|
142
|
+
$('G').classList.add('on');
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const result = await postJson('/api/process', getOptions());
|
|
146
|
+
|
|
147
|
+
if (result.logs) {
|
|
148
|
+
result.logs.forEach(l => log('G', l.type[0], l.message));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
$('L').classList.remove('on');
|
|
152
|
+
$('D').classList.add('on', result.success ? 'ok' : 'err');
|
|
153
|
+
$('dT').textContent = result.success ? 'Done' : 'Failed';
|
|
154
|
+
$('dM').textContent = result.success ? result.output : result.error;
|
|
155
|
+
} catch (e) {
|
|
156
|
+
log('G', 'e', e.message);
|
|
157
|
+
$('L').classList.remove('on');
|
|
158
|
+
$('D').classList.add('on', 'err');
|
|
159
|
+
$('dT').textContent = 'Error';
|
|
160
|
+
$('dM').textContent = e.message;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
</script>
|
|
164
|
+
</body>
|
|
165
|
+
</html>
|