Skip to content

Commit 8bdc4f1

Browse files
committed
Refactor Avatar Builder into modular files for improved structure and maintainability
Split the large avatar.js file into multiple smaller modules to improve project structure, readability, and long-term maintainability. No functionality changes were made. All logic, behavior, and APIs remain the same. The public interface (window.Avatars.initAvatarModal) is unchanged, so existing settings and account features continue to work without modification. Changes include: - Separated avatar logic into core, options, utilities, renderer, editor, and API files - Organized avatar part styles into dedicated modules - Improved script loading structure in settings.html - Fixed namespace wiring so shared functions are properly exposed between modules This refactor prepares the avatar system for easier future expansion and debugging.
1 parent 6f3dff9 commit 8bdc4f1

13 files changed

+2858
-2556
lines changed

apps/settings/avatar.js

Lines changed: 0 additions & 2135 deletions
This file was deleted.

apps/settings/avatar/avatar.api.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// avatar.api.js
2+
// Public API shim (keeps window.Avatars.initAvatarModal the same as before)
3+
(function () {
4+
const A = (window.__D4K_AVATAR__ = window.__D4K_AVATAR__ || {});
5+
window.Avatars = window.Avatars || {};
6+
window.Avatars.initAvatarModal = function initAvatarModal({ onPick } = {}) {
7+
return A.initEditor?.(onPick);
8+
};
9+
})();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// avatar.core.js
2+
// Auto-split from apps/settings/avatar.js (no behavior changes)
3+
(function () {
4+
const A = (window.__D4K_AVATAR__ = window.__D4K_AVATAR__ || {});
5+
/*
6+
Desktop4Kids - Avatar Builder Module
7+
Hair = PNG overlays from assets/avatars/avatar_hair_0X/avatar_hair_color.png
8+
*/
9+
10+
// =========================
11+
// DOM helpers
12+
// =========================
13+
const $ = (A.$ = (sel) => document.querySelector(sel));
14+
15+
const Modal = (A.Modal = {
16+
wrap: null,
17+
closeBtn: null,
18+
show() {
19+
this.wrap?.classList?.remove("hidden");
20+
this.wrap?.setAttribute("aria-hidden", "false");
21+
},
22+
hide() {
23+
this.wrap?.classList?.add("hidden");
24+
this.wrap?.setAttribute("aria-hidden", "true");
25+
},
26+
});
27+
28+
const toSvgDataUrl = (A.toSvgDataUrl = (svg) =>
29+
`data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svg)))}`);
30+
31+
// =========================
32+
// Hair PNG embed cache (required for data-URL SVG previews)
33+
// =========================
34+
const HairPngCache = (A.HairPngCache = new Map()); // src -> dataURL
35+
const HairPngLoading = (A.HairPngLoading = new Map()); // src -> Promise
36+
37+
async function pngToDataUrl(src) {
38+
if (HairPngCache.has(src)) return HairPngCache.get(src);
39+
if (HairPngLoading.has(src)) return HairPngLoading.get(src);
40+
41+
const p = (async () => {
42+
const res = await fetch(src, { cache: "force-cache" });
43+
if (!res.ok) throw new Error(`Failed to load hair PNG: ${src} (${res.status})`);
44+
const blob = await res.blob();
45+
46+
const dataUrl = await new Promise((resolve, reject) => {
47+
const r = new FileReader();
48+
r.onload = () => resolve(r.result);
49+
r.onerror = () => reject(r.error);
50+
r.readAsDataURL(blob);
51+
});
52+
53+
HairPngCache.set(src, dataUrl);
54+
HairPngLoading.delete(src);
55+
return dataUrl;
56+
})();
57+
58+
HairPngLoading.set(src, p);
59+
return p;
60+
}
61+
62+
A.pngToDataUrl = pngToDataUrl;
63+
A.Modal = Modal;
64+
A.$ = $;
65+
A.toSvgDataUrl = toSvgDataUrl;
66+
})();

0 commit comments

Comments
 (0)