mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-07-18 08:17:23 +00:00
feat: workshop redesign!
This commit is contained in:
parent
eedd9700f4
commit
394c667407
|
|
@ -168,68 +168,59 @@ const WorkshopView = memo(function WorkshopView() {
|
|||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ duration: config.animationsEnabled ? 0.3 : 0 }}
|
||||
className="flex flex-col items-center w-full max-w-5xl relative font-['Mojangles'] text-white select-none outline-none focus:outline-none"
|
||||
className="flex flex-col items-center w-full max-w-6xl relative font-['Mojangles'] text-white select-none outline-none focus:outline-none"
|
||||
>
|
||||
<div className="w-[95%] flex flex-col items-center mt-6">
|
||||
<div className="w-full flex items-end justify-between">
|
||||
<div className="flex items-end gap-0">
|
||||
{ALL_TABS.map((tab) => {
|
||||
const isActive = tab === activeTab;
|
||||
return (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => selectTab(tab)}
|
||||
className={`
|
||||
relative px-7 h-9 text-xl mc-text-shadow tracking-wide border-none outline-none cursor-pointer transition-none
|
||||
${isActive ? 'text-white z-20' : 'text-[#C0C0C0] hover:text-white z-10'}
|
||||
`}
|
||||
style={{
|
||||
backgroundImage: isActive
|
||||
? "url('/images/frame_background.png')"
|
||||
: "url('/images/backgroundframe.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
marginRight: '-1px',
|
||||
filter: isActive ? 'none' : 'brightness(0.8)',
|
||||
}}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<h2 className="text-2xl text-white mc-text-shadow mt-4 mb-6 border-b-2 border-[#373737] pb-2 w-[30%] max-w-[250px] text-center tracking-widest uppercase opacity-80 font-bold whitespace-nowrap px-4">
|
||||
Workshop
|
||||
</h2>
|
||||
|
||||
{!isSearchTab && (
|
||||
<span className="text-[#A0A0A0] text-sm uppercase tracking-widest mc-text-shadow mb-1 mr-1">
|
||||
Entries: {loading ? '...' : filteredItems.length}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2 mb-6 w-full flex-wrap px-4">
|
||||
{ALL_TABS.map((tab) => {
|
||||
const isActive = tab === activeTab;
|
||||
return (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => selectTab(tab)}
|
||||
className={`
|
||||
h-10 px-6 text-lg mc-text-shadow tracking-widest border-none outline-none cursor-pointer transition-all
|
||||
${isActive ? 'text-[#FFFF55] scale-105' : 'text-white hover:text-[#FFFF55] hover:scale-105'}
|
||||
`}
|
||||
style={{
|
||||
backgroundImage: isActive
|
||||
? "url('/images/button_highlighted.png')"
|
||||
: "url('/images/Button_Background.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
>
|
||||
{tab.toUpperCase()}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="p-2"></div>
|
||||
|
||||
<div
|
||||
className="w-[95%] flex-1 relative overflow-hidden"
|
||||
className="w-[98%] flex-1 relative overflow-hidden"
|
||||
style={{
|
||||
// backgroundImage: "url('/images/frame_background.png')",
|
||||
// backgroundSize: '100% 100%',
|
||||
// imageRendering: 'pixelated',
|
||||
minHeight: '430px',
|
||||
minHeight: '500px',
|
||||
}}
|
||||
>
|
||||
<AnimatePresence mode="wait">
|
||||
{isSearchTab ? (
|
||||
<motion.div
|
||||
key="search-tab"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="absolute inset-0 flex flex-col"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="absolute inset-0 flex flex-col pt-2"
|
||||
>
|
||||
<div className="flex items-center gap-3 px-4 pt-4 pb-3 border-b border-[#444]">
|
||||
<div className="flex items-center gap-3 px-6 pb-4">
|
||||
<div
|
||||
className="flex items-center flex-1 h-10 px-4"
|
||||
className="flex items-center flex-1 h-12 px-4 border-2 border-[#444] bg-black/40 rounded shadow-inner"
|
||||
style={{
|
||||
filter: 'brightness(0.8)',
|
||||
backgroundImage: "url('/images/Button_Background2.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
|
|
@ -237,93 +228,93 @@ const WorkshopView = memo(function WorkshopView() {
|
|||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => { setSearch(e.target.value); setFocusedIdx(null); }}
|
||||
placeholder="Search all workshop entries..."
|
||||
placeholder="ENTER KEYWORDS..."
|
||||
spellCheck={false}
|
||||
autoFocus
|
||||
className="bg-transparent border-none outline-none text-white text-base mc-text-shadow w-full placeholder-white font-['Mojangles']"
|
||||
className="bg-transparent border-none outline-none text-white text-lg mc-text-shadow w-full placeholder-white/40 font-['Mojangles'] tracking-widest"
|
||||
/>
|
||||
{search && (
|
||||
<button
|
||||
onClick={() => { setSearch(''); searchRef.current?.focus(); }}
|
||||
className="text-[#888] hover:text-white text-sm ml-2 bg-transparent border-none outline-none cursor-pointer"
|
||||
className="text-white/60 hover:text-white text-lg ml-2 bg-transparent border-none outline-none cursor-pointer mc-text-shadow"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[#A0A0A0] text-sm mc-text-shadow whitespace-nowrap">
|
||||
{search.trim() ? `${filteredItems.length} result${filteredItems.length !== 1 ? 's' : ''}` : ''}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{search.trim() && (
|
||||
<div ref={gridRef} className="flex-1 overflow-y-auto p-4">
|
||||
{filteredItems.length === 0 ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<span className="text-2xl text-[#E0E0E0] mc-text-shadow uppercase tracking-widest opacity-60">No results</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-3" style={{ gridTemplateColumns: `repeat(${COLS}, 1fr)` }}>
|
||||
{filteredItems.map((pkg, i) => (
|
||||
<PackageCard
|
||||
key={pkg.id}
|
||||
pkg={pkg}
|
||||
index={i}
|
||||
focused={focusedIdx === i}
|
||||
onHover={() => setFocusedIdx(i)}
|
||||
onClick={() => openModal(pkg)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div ref={gridRef} className="flex-1 overflow-y-auto p-6 scroll-smooth">
|
||||
{!search.trim() ? (
|
||||
<div className="flex flex-col items-center justify-center h-[200px] opacity-40">
|
||||
<span className="text-xl mc-text-shadow tracking-widest uppercase">Start typing to search...</span>
|
||||
</div>
|
||||
) : filteredItems.length === 0 ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<span className="text-2xl text-[#E0E0E0] mc-text-shadow uppercase tracking-widest opacity-60">No results</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-6" style={{ gridTemplateColumns: `repeat(${COLS}, 1fr)` }}>
|
||||
{filteredItems.map((pkg, i) => (
|
||||
<PackageCard
|
||||
key={pkg.id}
|
||||
pkg={pkg}
|
||||
index={i}
|
||||
focused={focusedIdx === i}
|
||||
onHover={() => setFocusedIdx(i)}
|
||||
onClick={() => openModal(pkg)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
) : loading ? (
|
||||
<motion.div key="loading" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
|
||||
className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-3xl text-[#E0E0E0] mc-text-shadow tracking-wider opacity-80 uppercase">Please wait</span>
|
||||
<span className="text-3xl text-[#FFFF55] mc-text-shadow tracking-widest animate-pulse uppercase">Searching Archives...</span>
|
||||
</motion.div>
|
||||
) : error ? (
|
||||
<motion.div key="error" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
|
||||
className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-xl text-red-400 mc-text-shadow uppercase tracking-widest">{error}</span>
|
||||
</motion.div>
|
||||
) : filteredItems.length === 0 ? (
|
||||
<motion.div key="empty" initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
|
||||
className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-2xl text-[#E0E0E0] mc-text-shadow uppercase tracking-widest opacity-60">No entries</span>
|
||||
<span className="text-xl text-red-500 mc-text-shadow uppercase tracking-widest">{error}</span>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
ref={gridRef}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="absolute inset-0 overflow-y-auto p-4"
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="absolute inset-0 overflow-y-auto p-6 scroll-smooth"
|
||||
>
|
||||
<div className="grid gap-3" style={{ gridTemplateColumns: `repeat(${COLS}, 1fr)` }}>
|
||||
{filteredItems.map((pkg, i) => (
|
||||
<PackageCard
|
||||
key={pkg.id}
|
||||
pkg={pkg}
|
||||
index={i}
|
||||
focused={focusedIdx === i}
|
||||
onHover={() => setFocusedIdx(i)}
|
||||
onClick={() => openModal(pkg)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{filteredItems.length === 0 ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<span className="text-2xl text-[#E0E0E0] mc-text-shadow uppercase tracking-widest opacity-40">Empty category</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-6" style={{ gridTemplateColumns: `repeat(${COLS}, 1fr)` }}>
|
||||
{filteredItems.map((pkg, i) => (
|
||||
<PackageCard
|
||||
key={pkg.id}
|
||||
pkg={pkg}
|
||||
index={i}
|
||||
focused={focusedIdx === i}
|
||||
onHover={() => setFocusedIdx(i)}
|
||||
onClick={() => openModal(pkg)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<div className="w-[95%] mt-3 mb-2 flex items-center gap-2">
|
||||
<div className="w-full mt-6 mb-4 flex justify-center">
|
||||
<button
|
||||
onClick={() => { playBackSound(); setActiveView('main'); }}
|
||||
className="w-40 h-10 flex items-center justify-center text-xl mc-text-shadow hover:text-[#FFFF55] text-white border-none outline-none transition-colors"
|
||||
className="w-72 h-10 flex items-center justify-center text-xl mc-text-shadow hover:text-[#FFFF55] text-white border-none outline-none transition-all"
|
||||
style={{ backgroundImage: "url('/images/Button_Background.png')", backgroundSize: '100% 100%', imageRendering: 'pixelated' }}
|
||||
onMouseEnter={(e) => { (e.currentTarget as HTMLButtonElement).style.backgroundImage = "url('/images/button_highlighted.png')"; }}
|
||||
onMouseLeave={(e) => { (e.currentTarget as HTMLButtonElement).style.backgroundImage = "url('/images/Button_Background.png')"; }}
|
||||
|
|
@ -356,35 +347,38 @@ function PackageCard({ pkg, index, focused, onHover, onClick }: {
|
|||
data-card={index}
|
||||
onMouseEnter={onHover}
|
||||
onClick={onClick}
|
||||
className={`flex flex-col cursor-pointer border-2 transition-none ${focused ? 'border-[#FFFF55]' : 'border-transparent'}`}
|
||||
className={`flex flex-col cursor-pointer transition-all border-2 ${focused ? 'border-[#FFFF55] scale-105 z-10' : 'border-[#333] hover:border-[#FFFF55]'} rounded-sm overflow-hidden bg-black/40`}
|
||||
style={{
|
||||
backgroundImage: "url('/images/frame_background.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
boxShadow: focused ? '0 0 20px rgba(255, 255, 85, 0.2)' : 'none',
|
||||
}}
|
||||
>
|
||||
<div className="w-full h-[100px] flex items-center justify-center overflow-hidden bg-black/30">
|
||||
<div className="w-full h-[120px] relative flex items-center justify-center overflow-hidden bg-black/50 border-b border-[#333]">
|
||||
{imgError ? (
|
||||
<span className="text-[#555] text-sm mc-text-shadow uppercase">No Image</span>
|
||||
<span className="text-[#555] text-sm mc-text-shadow uppercase tracking-widest">No Image</span>
|
||||
) : (
|
||||
<img src={thumbnailUrl} alt={pkg.name} className="w-full h-full object-contain object-center"
|
||||
<img src={thumbnailUrl} alt={pkg.name} className="w-full h-full object-cover group-hover:scale-110 transition-transform"
|
||||
style={{ imageRendering: 'pixelated' }} onError={() => setImgError(true)} />
|
||||
)}
|
||||
<div className="absolute top-1 right-1 flex gap-1">
|
||||
{pkg.category.slice(0, 1).map((c) => (
|
||||
<span key={c} className="text-[8px] bg-black/80 border border-[#555] px-1.5 py-0.5 text-[#FFFF55] mc-text-shadow uppercase tracking-tighter">{c}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col px-2 pt-2 pb-2 gap-0.5">
|
||||
<span className={`text-base mc-text-shadow leading-tight truncate ${focused ? 'text-[#FFFF55]' : 'text-white'}`}>
|
||||
<div className="flex flex-col p-3 gap-1 relative bg-gradient-to-b from-transparent to-black/20">
|
||||
<span className={`text-base mc-text-shadow leading-tight truncate font-bold tracking-wide ${focused ? 'text-[#FFFF55]' : 'text-white'}`}>
|
||||
{pkg.name}
|
||||
</span>
|
||||
<span className="text-xs text-[#A0A0A0] mc-text-shadow truncate">by {pkg.author}</span>
|
||||
<span className="text-xs text-[#888] mc-text-shadow leading-snug line-clamp-2 mt-0.5">{pkg.description}</span>
|
||||
<div className="flex items-center justify-between mt-1">
|
||||
<span className="text-[10px] text-[#A0A0A0] mc-text-shadow">v{pkg.version}</span>
|
||||
<div className="flex gap-1">
|
||||
{pkg.category.map((c) => (
|
||||
<span key={c} className="text-[9px] bg-black/60 border border-[#444] px-1 text-[#888] mc-text-shadow uppercase">{c}</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[10px] text-[#A0A0A0] mc-text-shadow uppercase tracking-widest">v{pkg.version}</span>
|
||||
<span className="text-[9px] text-[#55FF55] mc-text-shadow truncate opacity-80">{pkg.author}</span>
|
||||
</div>
|
||||
<p className="text-[10px] text-[#888] mc-text-shadow leading-[1.3] line-clamp-2 min-h-[2.6em] mt-1 italic">
|
||||
{pkg.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -424,92 +418,106 @@ function PackageModal({ pkg, onClose, playPressSound }: {
|
|||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-[200] flex items-center justify-center bg-black/70"
|
||||
className="fixed inset-0 z-[200] flex items-center justify-center bg-black/85 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.9, opacity: 0 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: 20, opacity: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex flex-col w-[560px] max-h-[80vh] overflow-hidden font-['Mojangles']"
|
||||
className="flex flex-col w-[640px] max-h-[85vh] overflow-hidden font-['Mojangles'] border-2 border-[#555] rounded-sm"
|
||||
style={{
|
||||
backgroundImage: "url('/images/frame_background.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
>
|
||||
<div className="w-full h-[200px] flex-shrink-0 bg-black/40 overflow-hidden relative">
|
||||
<div className="w-full h-[240px] flex-shrink-0 bg-black/60 overflow-hidden relative border-b border-[#444]">
|
||||
{imgError ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<span className="text-[#555] mc-text-shadow uppercase">No Image</span>
|
||||
<div className="absolute inset-0 flex items-center justify-center opacity-20">
|
||||
<span className="text-4xl mc-text-shadow uppercase tracking-widest">No Image</span>
|
||||
</div>
|
||||
) : (
|
||||
<img src={thumbnailUrl} alt={pkg.name} className="w-full h-full object-contain object-center"
|
||||
<img src={thumbnailUrl} alt={pkg.name} className="w-full h-full object-cover"
|
||||
style={{ imageRendering: 'pixelated' }} onError={() => setImgError(true)} />
|
||||
)}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent" />
|
||||
<div className="absolute bottom-3 left-4 right-4">
|
||||
<span className="text-2xl text-white mc-text-shadow block leading-tight">{pkg.name}</span>
|
||||
<span className="text-sm text-[#FFFF55] mc-text-shadow">by {pkg.author}</span>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/90 via-black/40 to-transparent" />
|
||||
<div className="absolute bottom-4 left-6 right-6">
|
||||
<span className="text-3xl text-white mc-text-shadow block leading-tight tracking-wide font-bold">{pkg.name}</span>
|
||||
<span className="text-base text-[#FFFF55] mc-text-shadow uppercase tracking-widest opacity-90">By {pkg.author}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col p-5 gap-3 overflow-y-auto flex-1">
|
||||
<p className="text-sm text-[#C0C0C0] mc-text-shadow leading-relaxed">{pkg.description}</p>
|
||||
<div className="flex flex-col p-6 gap-6 overflow-y-auto flex-1">
|
||||
<div className="space-y-2">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-[0.2em] font-bold">Project Description</span>
|
||||
<p className="text-sm text-[#C0C0C0] mc-text-shadow leading-relaxed italic opacity-90">{pkg.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 pt-1 border-t border-[#555]">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-widest">Version</span>
|
||||
<span className="text-sm text-white mc-text-shadow">v{pkg.version}</span>
|
||||
<div className="grid grid-cols-2 gap-8 pt-4 border-t border-[#333]">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-[0.2em] font-bold">Metadata</span>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-[#888] mc-text-shadow">Version:</span>
|
||||
<span className="text-white mc-text-shadow">v{pkg.version}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs">
|
||||
<span className="text-[#888] mc-text-shadow">Package ID:</span>
|
||||
<span className="text-[#55FF55] mc-text-shadow truncate ml-2">{pkg.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-widest">Categories</span>
|
||||
<div className="flex gap-1">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-[0.2em] font-bold">Categories</span>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{pkg.category.map((c) => (
|
||||
<span key={c} className="text-xs bg-black/60 border border-[#555] px-2 py-0.5 text-[#A0A0A0] mc-text-shadow uppercase">{c}</span>
|
||||
<span key={c} className="text-[10px] bg-black/60 border border-[#444] px-2 py-0.5 text-[#A0A0A0] mc-text-shadow uppercase tracking-widest">{c}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{Object.keys(pkg.zips).length > 0 && (
|
||||
<div className="flex flex-col gap-1 pt-1 border-t border-[#555]">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-widest">Files</span>
|
||||
{Object.entries(pkg.zips).map(([file, dest]) => (
|
||||
<div key={file} className="flex items-center justify-between gap-2">
|
||||
<span className="text-xs text-[#A0A0A0] mc-text-shadow">{file}</span>
|
||||
{dest && <span className="text-[10px] text-[#666] mc-text-shadow truncate max-w-[200px]">{dest}</span>}
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-col gap-3 pt-4 border-t border-[#333]">
|
||||
<span className="text-[10px] text-[#666] mc-text-shadow uppercase tracking-[0.2em] font-bold">Files</span>
|
||||
<div className="space-y-1.5">
|
||||
{Object.entries(pkg.zips).map(([file, dest]) => (
|
||||
<div key={file} className="flex items-center justify-between gap-4 bg-black/20 p-2 rounded-sm border border-[#222]">
|
||||
<span className="text-xs text-[#A0A0A0] mc-text-shadow font-mono">{file}</span>
|
||||
{dest && <span className="text-[9px] text-[#fff] mc-text-shadow truncate uppercase tracking-tighter">{dest}</span>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-3 pt-2">
|
||||
<div className="flex items-center gap-4 pt-4 mt-auto">
|
||||
<button
|
||||
onMouseEnter={() => setModalFocus('install')}
|
||||
onClick={() => setShowInstall(true)}
|
||||
className={`flex-1 h-11 flex items-center justify-center text-xl mc-text-shadow border-none outline-none cursor-pointer transition-none ${modalFocus === 'install' ? 'text-[#FFFF55]' : 'text-white'}`}
|
||||
className={`flex-1 h-12 flex items-center justify-center text-xl mc-text-shadow border-none outline-none cursor-pointer transition-all ${modalFocus === 'install' ? 'text-[#FFFF55] scale-105' : 'text-white'}`}
|
||||
style={{
|
||||
backgroundImage: modalFocus === 'install' ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
>
|
||||
Install
|
||||
INSTALL
|
||||
</button>
|
||||
<button
|
||||
onMouseEnter={() => setModalFocus('close')}
|
||||
onClick={onClose}
|
||||
className={`w-32 h-11 flex items-center justify-center text-xl mc-text-shadow border-none outline-none cursor-pointer transition-none ${modalFocus === 'close' ? 'text-[#FFFF55]' : 'text-white'}`}
|
||||
className={`w-36 h-12 flex items-center justify-center text-xl mc-text-shadow border-none outline-none cursor-pointer transition-all ${modalFocus === 'close' ? 'text-[#FFFF55] scale-105' : 'text-white'}`}
|
||||
style={{
|
||||
backgroundImage: modalFocus === 'close' ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
>
|
||||
Close
|
||||
BACK
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -589,21 +597,21 @@ function InstallModal({ pkg, onClose, playPressSound }: {
|
|||
onClick={status !== 'installing' ? onClose : undefined}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.9, opacity: 0 }}
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
exit={{ y: 20, opacity: 0 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="flex flex-col w-[480px] font-['Mojangles'] text-white"
|
||||
className="flex flex-col w-[520px] font-['Mojangles'] text-white border-2 border-[#555] rounded-sm overflow-hidden"
|
||||
style={{
|
||||
backgroundImage: "url('/images/frame_background.png')",
|
||||
backgroundSize: '100% 100%',
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
>
|
||||
<div className="p-4 border-b border-[#555] bg-black/30">
|
||||
<span className="text-xl mc-text-shadow block">Install to Edition</span>
|
||||
<span className="text-sm text-[#A0A0A0] mc-text-shadow">Select an installed edition for "{pkg.name}"</span>
|
||||
<div className="p-6 border-b border-[#555] bg-black/60">
|
||||
<span className="text-2xl mc-text-shadow block font-bold tracking-wide">INSTALL CONTENT</span>
|
||||
<span className="text-sm text-[#A0A0A0] mc-text-shadow uppercase tracking-widest opacity-80 mt-1">Target Edition for "{pkg.name}"</span>
|
||||
</div>
|
||||
|
||||
<div className="p-4 flex flex-col gap-2 max-h-[300px] overflow-y-auto">
|
||||
|
|
|
|||
Loading…
Reference in a new issue