feat: builtin capes

This commit is contained in:
neoapps-dev 2026-08-13 03:34:25 +03:00
parent 58aff0db3a
commit d49622495f
3 changed files with 39 additions and 13 deletions

BIN
public/Capes/4J.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

View file

@ -76,10 +76,15 @@ const DEFAULT_SKINS: SavedSkin[] = [
id: "tranqlmao",
name: "Tranq",
url: "/Skins/tranq.png",
isSlim: false,
isSlim: true,
},
];
const DEFAULT_CAPES: SavedCape[] = [
{ id: "4j", name: "4J Studios", url: "/Capes/4J.png" },
{ id: "unused2", name: "Unused Cape 2", url: "/Capes/Unused_Cape_2.png" },
];
const HeadPreview = memo(function HeadPreview({ src }: { src: string }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
@ -135,13 +140,17 @@ const SkinsView = memo(function SkinsView() {
"lce-custom-capes",
[],
);
const savedCapes = [
...DEFAULT_CAPES,
...storedCapes.filter((c) => !DEFAULT_CAPES.some((d) => d.id === c.id)),
];
const [activeCapeId, setActiveCapeId] = useState<string | null>(null);
const TOP_BUTTONS_COUNT = viewMode === "skin" ? 3 : 3;
const SKINS_START_INDEX = TOP_BUTTONS_COUNT;
const BACK_BUTTON_INDEX =
SKINS_START_INDEX +
(viewMode === "skin" ? savedSkins.length : storedCapes.length);
(viewMode === "skin" ? savedSkins.length : savedCapes.length);
const ITEM_COUNT = BACK_BUTTON_INDEX + 1;
const setSavedSkins = (
@ -243,10 +252,10 @@ const SkinsView = memo(function SkinsView() {
useEffect(() => {
if (!activeCapeId) {
const match = storedCapes.find((c) => c.url === capeUrl);
const match = savedCapes.find((c) => c.url === capeUrl);
if (match) setActiveCapeId(match.id);
}
}, [activeCapeId, storedCapes, capeUrl]);
}, [activeCapeId, savedCapes, capeUrl]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
@ -333,7 +342,7 @@ const SkinsView = memo(function SkinsView() {
setFocusIndex(0);
} else if (focusIndex === BACK_BUTTON_INDEX) {
const itemCount =
viewMode === "cape" ? storedCapes.length + 1 : savedSkins.length;
viewMode === "cape" ? savedCapes.length + 1 : savedSkins.length;
setFocusIndex(SKINS_START_INDEX + itemCount - 1);
} else if (focusIndex >= SKINS_START_INDEX) {
const rowCount = 4;
@ -351,7 +360,18 @@ const SkinsView = memo(function SkinsView() {
playPressSound();
setViewMode(viewMode === "skin" ? "cape" : "skin");
} else if (focusIndex < BACK_BUTTON_INDEX) {
handleSkinSelect(savedSkins[focusIndex - SKINS_START_INDEX]);
if (viewMode === "cape") {
const capeIdx = focusIndex - SKINS_START_INDEX;
if (capeIdx === 0) {
playPressSound();
setCapeUrl(null);
setActiveCapeId(null);
} else {
handleCapeSelect(savedCapes[capeIdx - 1]);
}
} else {
handleSkinSelect(savedSkins[focusIndex - SKINS_START_INDEX]);
}
} else {
playBackSound();
setActiveView("main");
@ -363,7 +383,7 @@ const SkinsView = memo(function SkinsView() {
}, [
focusIndex,
savedSkins.length,
storedCapes.length,
savedCapes.length,
playBackSound,
setActiveView,
playPressSound,
@ -414,6 +434,9 @@ const SkinsView = memo(function SkinsView() {
const isDefaultSkin = (id: string | null) =>
DEFAULT_SKINS.some((d) => d.id === id);
const isDefaultCape = (id: string | null) =>
DEFAULT_CAPES.some((d) => d.id === id);
const handleDeleteActive = () => {
if (!activeSkinId || isDefaultSkin(activeSkinId)) return;
playPressSound();
@ -457,7 +480,7 @@ const SkinsView = memo(function SkinsView() {
};
const handleDeleteActiveCape = () => {
if (!activeCapeId) return;
if (!activeCapeId || isDefaultCape(activeCapeId)) return;
playPressSound();
const updatedCapes = storedCapes.filter((c) => c.id !== activeCapeId);
setStoredCapes(updatedCapes);
@ -476,6 +499,8 @@ const SkinsView = memo(function SkinsView() {
isDefaultSkin(activeSkinId) ||
(!activeSkinId && skinUrl === "/images/Default.png");
const isActiveCapeDefault = !activeCapeId && !capeUrl;
const isCapeDeleteDisabled =
isActiveCapeDefault || isDefaultCape(activeCapeId);
return (
<motion.div
@ -518,7 +543,7 @@ const SkinsView = memo(function SkinsView() {
data-index="1"
onMouseEnter={() => {
if (viewMode === "skin" && !isActiveDefault) setFocusIndex(1);
else if (viewMode === "cape" && !isActiveCapeDefault)
else if (viewMode === "cape" && !isCapeDeleteDisabled)
setFocusIndex(1);
}}
onClick={() => {
@ -528,7 +553,7 @@ const SkinsView = memo(function SkinsView() {
}}
className={`w-40 h-10 flex items-center justify-center transition-colors text-2xl mc-text-shadow outline-none border-none ${
(viewMode === "skin" && isActiveDefault) ||
(viewMode === "cape" && isActiveCapeDefault)
(viewMode === "cape" && isCapeDeleteDisabled)
? "text-gray-400 opacity-80 cursor-not-allowed"
: focusIndex === 1
? "text-[#FFFF55]"
@ -537,7 +562,7 @@ const SkinsView = memo(function SkinsView() {
style={{
backgroundImage:
(viewMode === "skin" && isActiveDefault) ||
(viewMode === "cape" && isActiveCapeDefault)
(viewMode === "cape" && isCapeDeleteDisabled)
? "url('/images/Button_Background2.png')"
: focusIndex === 1
? "url('/images/button_highlighted.png')"
@ -673,7 +698,7 @@ const SkinsView = memo(function SkinsView() {
No Cape
</span>
</div>
{storedCapes.map((cape, i) => {
{savedCapes.map((cape, i) => {
const idx = SKINS_START_INDEX + 1 + i;
const isActive = activeCapeId
? activeCapeId === cape.id
@ -707,9 +732,10 @@ const SkinsView = memo(function SkinsView() {
onChange={(e) =>
handleCapeNameChange(cape.id, e.target.value)
}
className={`bg-transparent text-center outline-none border-none text-base mc-text-shadow w-full truncate transition-colors relative z-10 ${isActive || isFocused ? "text-[#FFFF55]" : "text-white"}`}
className={`bg-transparent text-center outline-none border-none text-base mc-text-shadow w-full truncate transition-colors relative z-10 ${isActive || isFocused ? "text-[#FFFF55]" : "text-white"} ${isDefaultCape(cape.id) ? "pointer-events-none" : ""}`}
onClick={(e) => e.stopPropagation()}
spellCheck={false}
readOnly={isDefaultCape(cape.id)}
/>
</div>
);