feat: extended description and required versions

This commit is contained in:
neoapps-dev 2026-07-07 17:02:20 +03:00
parent 2299f9c4be
commit 94abd044dc
9 changed files with 2489 additions and 247 deletions

View file

@ -5,6 +5,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="piston.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preload" href="/fonts/Mojangles.ttf" as="font" type="font/truetype" crossorigin>
<title>Piston - Emerald Workshop</title>
</head>

View file

@ -13,7 +13,11 @@
"@octokit/rest": "^22.0.1",
"lucide-react": "^1.8.0",
"react": "^19.2.4",
"react-dom": "^19.2.4"
"react-dom": "^19.2.4",
"react-markdown": "^10.1.0",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"remark-gfm": "^4.0.1"
},
"devDependencies": {
"@eslint/js": "^9.39.4",

File diff suppressed because it is too large Load diff

BIN
public/fonts/Mojangles.ttf Normal file

Binary file not shown.

View file

@ -1,5 +1,7 @@
import { useState, useEffect } from 'react';
import { fetchRegistry, getRawFileUrl } from './api/registry';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { fetchRegistry, getRawFileUrl, BUILTIN_EDITIONS } from './api/registry';
import type { RegistryResponse, MetaJson } from './api/types';
import { Key, X, Download, Upload, ExternalLink } from 'lucide-react';
import UploadModal from './UploadModal';
@ -226,6 +228,31 @@ function App() {
{selectedItem.description}
</div>
{selectedItem.extended_description && selectedItem.extended_description.trim() !== '' && (
<div className="mc-extended-description">
<h3>Details</h3>
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{selectedItem.extended_description}
</ReactMarkdown>
</div>
)}
{selectedItem.required_versions && selectedItem.required_versions.length > 0 && (
<div style={{ marginTop: '1.5rem' }}>
<h3 style={{ fontSize: '1rem', textTransform: 'uppercase', color: '#555', textShadow: '1px 1px 0px rgba(255,255,255,0.4)', marginBottom: '0.5rem' }}>Required Editions</h3>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
{selectedItem.required_versions.map((verId) => {
const ed = BUILTIN_EDITIONS.find((e) => e.id === verId);
return (
<span key={verId} style={{ background: '#373737', color: '#fff', border: '2px solid #1d1d1d', padding: '0.3rem 0.6rem', fontSize: '0.85rem' }}>
{ed?.name || verId}
</span>
);
})}
</div>
</div>
)}
<h3 style={{ marginTop: '2rem' }}>Downloads & Installation</h3>
<div className="mc-download-list">
{Object.entries(selectedItem.zips).map(([zipName, extractPath]) => (

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
import type { RegistryResponse } from './types';
const REGISTRY_URL = 'https://raw.githubusercontent.com/LCE-Hub/LCE-Workshop/refs/heads/main/registry.json';
const VERSIONS_URL = 'https://raw.githubusercontent.com/LCE-Hub/LCE-Workshop/refs/heads/main/versions.json';
export async function fetchRegistry(): Promise<RegistryResponse> {
const response = await fetch(REGISTRY_URL);
if (!response.ok) {
@ -8,6 +9,41 @@ export async function fetchRegistry(): Promise<RegistryResponse> {
return response.json();
}
export interface VersionListEntry {
id: string;
name: string;
author: string;
description: string;
extended_description: string;
category: string[];
thumbnail: string;
logo: string;
url: string;
version: string;
}
export interface VersionListResponse {
generated_at: string;
count: number;
versionlist: VersionListEntry[];
}
export async function fetchVersionList(): Promise<VersionListResponse> {
const response = await fetch(VERSIONS_URL);
if (!response.ok) {
throw new Error('Failed to fetch versions');
}
return response.json();
}
export function getRawFileUrl(modId: string, filename: string): string {
return `https://raw.githubusercontent.com/LCE-Hub/LCE-Workshop/refs/heads/main/${modId}/${filename}`;
}
export const BUILTIN_EDITIONS: { id: string; name: string }[] = [
{ id: 'revelations', name: 'Revelations' },
{ id: 'legacy_evolved', name: 'neoLegacy' },
{ id: '360revived', name: '360Revived' },
{ id: 'legacy_nether_fork', name: 'Hellish Ends' },
{ id: 'moon_edition', name: 'Minecraft: Moon Edition' },
];

View file

@ -8,6 +8,8 @@ export interface MetaJson {
thumbnail: string;
zips: Record<string, string>;
version: string;
dependencies?: string[];
required_versions?: string[];
}
export interface RegistryResponse {

View file

@ -1,267 +1,480 @@
@import url('https://fonts.googleapis.com/css2?family=VT323&display=swap');
@font-face {
font-family: "Minecraft";
src: url("/fonts/Mojangles.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
:root {
--mc-bg: #c6c6c6;
--mc-dark: #373737;
--mc-border-light: #ffffff;
--mc-border-dark: #555555;
--mc-border-darker: #373737;
--mc-hover: #7b9fe7;
--mc-bg: #c6c6c6;
--mc-dark: #373737;
--mc-border-light: #ffffff;
--mc-border-dark: #555555;
--mc-border-darker: #373737;
--mc-hover: #7b9fe7;
--mc-btn: #c6c6c6;
--mc-btn-hover: #7b9fe7;
--mc-btn-active: #5f86c8;
--mc-btn: #c6c6c6;
--mc-btn-hover: #7b9fe7;
--mc-btn-active: #5f86c8;
}
* {
box-sizing: border-box;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: 'Minecraft', 'VT323', ui-monospace, monospace;
background-image: url('/bedrock_bg.png');
background-size: cover;
background-position: center;
background-attachment: fixed;
margin: 0;
padding: 0;
font-family: "Minecraft", ui-monospace, monospace;
-webkit-font-smoothing: none;
-moz-osx-font-smoothing: unset;
background-image: url("/bedrock_bg.png");
background-size: cover;
background-position: center;
background-attachment: fixed;
}
.mc-container {
max-width: 1200px;
margin: 2rem auto;
background-color: rgba(198, 198, 198, 0.95);
color: #373737;
border: 6px solid var(--mc-border-darker);
box-shadow:
inset 6px 6px 0 var(--mc-border-light),
inset -6px -6px 0 var(--mc-border-dark),
0 10px 30px rgba(0, 0, 0, 0.8);
padding: 2rem;
image-rendering: auto;
max-width: 1200px;
margin: 2rem auto;
background-color: rgba(198, 198, 198, 0.95);
color: #373737;
border: 6px solid var(--mc-border-darker);
box-shadow:
inset 6px 6px 0 var(--mc-border-light),
inset -6px -6px 0 var(--mc-border-dark),
0 10px 30px rgba(0, 0, 0, 0.8);
padding: 2rem;
image-rendering: auto;
}
h1,
h2,
h3 {
margin-top: 0;
text-transform: uppercase;
text-shadow: 2px 2px 0px #888;
margin-top: 0;
text-transform: uppercase;
text-shadow: 2px 2px 0px #888;
}
.mc-button {
background-color: var(--mc-btn);
color: #373737;
border: 4px solid var(--mc-border-darker);
box-shadow:
inset 4px 4px 0 var(--mc-border-light),
inset -4px -4px 0 var(--mc-border-dark);
padding: 0.5rem 1.5rem;
font-family: inherit;
font-size: 1.2rem;
cursor: pointer;
text-transform: uppercase;
transition: all 0.1s;
background-color: var(--mc-btn);
color: #373737;
border: 4px solid var(--mc-border-darker);
box-shadow:
inset 4px 4px 0 var(--mc-border-light),
inset -4px -4px 0 var(--mc-border-dark);
padding: 0.5rem 1.5rem;
font-family: inherit;
font-size: 1.2rem;
cursor: pointer;
text-transform: uppercase;
transition: all 0.1s;
}
.mc-button:hover {
background-color: var(--mc-btn-hover);
color: white;
border: 4px solid #fff;
box-shadow:
inset 4px 4px 0 #a9c6f9,
inset -4px -4px 0 #4f74bb;
text-shadow: 2px 2px 0px #373737;
background-color: var(--mc-btn-hover);
color: white;
border: 4px solid #fff;
box-shadow:
inset 4px 4px 0 #a9c6f9,
inset -4px -4px 0 #4f74bb;
text-shadow: 2px 2px 0px #373737;
}
.mc-button:active {
background-color: var(--mc-btn-active);
box-shadow:
inset 4px 4px 0 var(--mc-border-dark),
inset -4px -4px 0 var(--mc-border-light);
background-color: var(--mc-btn-active);
box-shadow:
inset 4px 4px 0 var(--mc-border-dark),
inset -4px -4px 0 var(--mc-border-light);
}
.mc-input {
background-color: #000;
color: white;
border: 4px solid #fff;
box-shadow:
inset 4px 4px 0 #555,
inset -4px -4px 0 #a0a0a0;
padding: 0.5rem;
font-family: inherit;
font-size: 1.2rem;
width: 100%;
background-color: #000;
color: white;
border: 4px solid #fff;
box-shadow:
inset 4px 4px 0 #555,
inset -4px -4px 0 #a0a0a0;
padding: 0.5rem;
font-family: inherit;
font-size: 1.2rem;
width: 100%;
}
.mc-input:focus {
outline: none;
border-color: #a0a0a0;
outline: none;
border-color: #a0a0a0;
}
::-webkit-scrollbar {
width: 16px;
background-color: #000;
width: 16px;
background-color: #000;
}
::-webkit-scrollbar-thumb {
background-color: var(--mc-btn);
border: 3px solid var(--mc-btn);
border-top-color: var(--mc-border-light);
border-left-color: var(--mc-border-light);
border-bottom-color: var(--mc-border-dark);
border-right-color: var(--mc-border-dark);
background-color: var(--mc-btn);
border: 3px solid var(--mc-btn);
border-top-color: var(--mc-border-light);
border-left-color: var(--mc-border-light);
border-bottom-color: var(--mc-border-dark);
border-right-color: var(--mc-border-dark);
}
.mc-card {
background-color: #8b8b8b;
border: 4px solid var(--mc-border-darker);
box-shadow:
inset 4px 4px 0 #a0a0a0,
inset -4px -4px 0 #555;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
background-color: #8b8b8b;
border: 4px solid var(--mc-border-darker);
box-shadow:
inset 4px 4px 0 #a0a0a0,
inset -4px -4px 0 #555;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.mc-card img {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border: 4px solid #373737;
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border: 4px solid #373737;
}
.mc-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
}
.mc-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
border-bottom: 4px solid #373737;
padding-bottom: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
border-bottom: 4px solid #373737;
padding-bottom: 1rem;
}
.mc-badge {
background: #373737;
color: white;
padding: 0.2rem 0.5rem;
font-size: 0.9rem;
border: 2px solid #1d1d1d;
background: #373737;
color: white;
padding: 0.2rem 0.5rem;
font-size: 0.9rem;
border: 2px solid #1d1d1d;
}
.mc-detail-view {
display: flex;
flex-direction: column;
gap: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.mc-detail-header {
display: flex;
align-items: center;
border-bottom: 4px solid #555;
padding-bottom: 1rem;
display: flex;
align-items: center;
border-bottom: 4px solid #555;
padding-bottom: 1rem;
}
.mc-detail-content {
display: grid;
grid-template-columns: 400px 1fr;
gap: 2rem;
display: grid;
grid-template-columns: 400px 1fr;
gap: 2rem;
}
.mc-detail-img {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border: 4px solid #373737;
box-shadow: inset 4px 4px 0 #a0a0a0, inset -4px -4px 0 #555;
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border: 4px solid #373737;
box-shadow:
inset 4px 4px 0 #a0a0a0,
inset -4px -4px 0 #555;
}
@media (max-width: 900px) {
.mc-detail-content {
grid-template-columns: 1fr;
gap: 1rem;
}
.mc-detail-content {
grid-template-columns: 1fr;
gap: 1rem;
}
}
.mc-title {
font-size: 2.5rem;
margin-bottom: 0.5rem;
color: #373737;
text-shadow: 2px 2px 0px rgba(255, 255, 255, 0.6);
font-size: 2.5rem;
margin-bottom: 0.5rem;
color: #373737;
text-shadow: 2px 2px 0px rgba(255, 255, 255, 0.6);
}
.mc-version {
font-size: 1.2rem;
color: #555;
font-size: 1.2rem;
color: #555;
}
.mc-author {
font-size: 1.2rem;
color: #555;
margin-bottom: 1rem;
font-size: 1.2rem;
color: #555;
margin-bottom: 1rem;
}
.mc-description {
background: rgba(0, 0, 0, 0.1);
padding: 1rem;
border-left: 4px solid #555;
font-size: 1.1rem;
line-height: 1.5;
background: rgba(0, 0, 0, 0.1);
padding: 1rem;
border-left: 4px solid #555;
font-size: 1.1rem;
line-height: 1.5;
}
.mc-download-list {
background: #373737;
color: white;
border: 4px solid #1d1d1d;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
background: #373737;
color: white;
border: 4px solid #1d1d1d;
padding: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.mc-download-item {
display: flex;
justify-content: space-between;
align-items: center;
background: #373737;
padding: 1rem;
border: 2px solid #1d1d1d;
box-shadow: inset 2px 2px 0 #555, inset -2px -2px 0 #111;
display: flex;
justify-content: space-between;
align-items: center;
background: #373737;
padding: 1rem;
border: 2px solid #1d1d1d;
box-shadow:
inset 2px 2px 0 #555,
inset -2px -2px 0 #111;
}
.mc-download-info strong {
color: #7b9fe7;
font-size: 1.2rem;
color: #7b9fe7;
font-size: 1.2rem;
}
.mc-download-path {
font-size: 0.9rem;
color: #aaa;
margin-top: 0.5rem;
font-size: 0.9rem;
color: #aaa;
margin-top: 0.5rem;
}
.mc-download-path code {
color: #fff;
background: #111;
padding: 0.2rem 0.4rem;
color: #fff;
background: #111;
padding: 0.2rem 0.4rem;
}
.mc-badges {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.mc-download-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 0.5rem;
}
display: flex;
align-items: center;
justify-content: center;
padding: 0.5rem;
}
.mc-extended-description {
margin-top: 2rem;
background: rgba(0, 0, 0, 0.1);
padding: 1rem;
border-left: 4px solid #555;
font-size: 1.1rem;
line-height: 1.5;
}
.mc-extended-description h1,
.mc-extended-description h2,
.mc-extended-description h3,
.mc-extended-description h4 {
color: #1d1d1d;
text-shadow: 1px 1px 0px rgba(255, 255, 255, 0.4);
margin: 1rem 0 0.5rem;
text-transform: uppercase;
}
.mc-extended-description h1 {
font-size: 1.8rem;
}
.mc-extended-description h2 {
font-size: 1.5rem;
}
.mc-extended-description h3 {
font-size: 1.3rem;
}
.mc-extended-description p {
margin: 0.5rem 0;
}
.mc-extended-description ul,
.mc-extended-description ol {
padding-left: 1.5rem;
margin: 0.5rem 0;
}
.mc-extended-description li {
margin: 0.25rem 0;
}
.mc-extended-description code {
background: #373737;
color: #fff;
padding: 0.15rem 0.4rem;
font-size: 0.9rem;
}
.mc-extended-description pre {
background: #373737;
color: #fff;
padding: 1rem;
overflow-x: auto;
border: 4px solid #1d1d1d;
margin: 0.75rem 0;
}
.mc-extended-description pre code {
background: none;
padding: 0;
}
.mc-extended-description a {
color: #2e5da0;
text-decoration: underline;
}
.mc-extended-description blockquote {
border-left: 4px solid #888;
margin: 0.75rem 0;
padding: 0.5rem 1rem;
background: rgba(0, 0, 0, 0.05);
color: #555;
}
.mc-extended-description table {
border-collapse: collapse;
width: 100%;
margin: 0.75rem 0;
}
.mc-extended-description th,
.mc-extended-description td {
border: 2px solid #555;
padding: 0.5rem;
text-align: left;
}
.mc-extended-description th {
background: #373737;
color: #fff;
text-transform: uppercase;
font-size: 0.9rem;
}
.mc-extended-description td {
background: rgba(0, 0, 0, 0.05);
}
.mc-extended-description img {
max-width: 100%;
border: 4px solid #373737;
margin: 0.75rem 0;
}
.mc-extended-description hr {
border: none;
border-top: 4px solid #555;
margin: 1.5rem 0;
}
.mc-markdown-preview {
line-height: 1.5;
}
.mc-markdown-preview h1,
.mc-markdown-preview h2,
.mc-markdown-preview h3,
.mc-markdown-preview h4 {
color: #fff;
text-shadow: 2px 2px 0px #1d1d1d;
margin: 1rem 0 0.5rem;
}
.mc-markdown-preview h1 {
font-size: 1.5rem;
}
.mc-markdown-preview h2 {
font-size: 1.3rem;
}
.mc-markdown-preview h3 {
font-size: 1.1rem;
}
.mc-markdown-preview p {
margin: 0.5rem 0;
}
.mc-markdown-preview ul,
.mc-markdown-preview ol {
padding-left: 1.5rem;
margin: 0.5rem 0;
}
.mc-markdown-preview li {
margin: 0.25rem 0;
}
.mc-markdown-preview code {
background: #1d1d1d;
color: #fff;
padding: 0.15rem 0.4rem;
font-size: 0.85rem;
}
.mc-markdown-preview pre {
background: #1d1d1d;
color: #fff;
padding: 1rem;
overflow-x: auto;
border: 2px solid #000;
margin: 0.75rem 0;
}
.mc-markdown-preview pre code {
background: none;
padding: 0;
}
.mc-markdown-preview a {
color: #7b9fe7;
text-decoration: underline;
}
.mc-markdown-preview blockquote {
border-left: 4px solid #888;
margin: 0.75rem 0;
padding: 0.5rem 1rem;
color: #aaa;
}
.mc-markdown-preview table {
border-collapse: collapse;
width: 100%;
margin: 0.75rem 0;
}
.mc-markdown-preview th,
.mc-markdown-preview td {
border: 2px solid #555;
padding: 0.4rem;
text-align: left;
}
.mc-markdown-preview th {
background: #1d1d1d;
color: #fff;
text-transform: uppercase;
font-size: 0.85rem;
}
.mc-markdown-preview img {
max-width: 100%;
border: 2px solid #555;
margin: 0.75rem 0;
}
.mc-markdown-preview hr {
border: none;
border-top: 2px solid #555;
margin: 1rem 0;
}