modified: app.py

new file:   templates/itemeditor_command_storage.html
	modified:   versions/version.json
This commit is contained in:
SimolZimol
2026-01-07 03:45:06 +01:00
parent 3dd524bd84
commit 9e0a1abb53
3 changed files with 790 additions and 39 deletions

87
app.py
View File

@@ -1,10 +1,26 @@
import os import os
import json import json
import uuid
from pathlib import Path from pathlib import Path
from flask import Flask, render_template, jsonify, send_from_directory from datetime import datetime, timedelta
from threading import Lock
from flask import Flask, render_template, jsonify, send_from_directory, request
app = Flask(__name__) app = Flask(__name__)
# In-memory storage for commands (thread-safe)
command_storage = {}
storage_lock = Lock()
def cleanup_expired_commands():
"""Remove expired commands from storage"""
with storage_lock:
now = datetime.now()
expired_keys = [key for key, value in command_storage.items()
if datetime.fromisoformat(value['expires_at']) < now]
for key in expired_keys:
del command_storage[key]
# Load projects from version.json # Load projects from version.json
def load_projects(): def load_projects():
"""Load all projects from version.json""" """Load all projects from version.json"""
@@ -228,6 +244,75 @@ def project_detail(project_id):
return render_template('project_detail.html', project=project) return render_template('project_detail.html', project=project)
# Item Editor Command Storage
@app.route('/projects/itemeditor/storage')
def itemeditor_command_storage():
"""Item Editor Command Storage page"""
return render_template('itemeditor_command_storage.html')
@app.route('/projects/itemeditor/storage', methods=['POST'])
def store_command():
"""Store a command and return UUID link"""
cleanup_expired_commands()
try:
data = request.get_json()
command = data.get('command', '').strip()
if not command:
return jsonify({'success': False, 'error': 'Command is required'}), 400
if len(command) > 10000:
return jsonify({'success': False, 'error': 'Command exceeds maximum length of 10,000 characters'}), 400
# Generate UUID
command_uuid = str(uuid.uuid4())
# Calculate expiry (30 minutes)
created_at = datetime.now()
expires_at = created_at + timedelta(minutes=30)
# Store command
with storage_lock:
command_storage[command_uuid] = {
'command': command,
'created_at': created_at.isoformat(),
'expires_at': expires_at.isoformat()
}
# Generate URL
base_url = request.host_url.rstrip('/')
retrieval_url = f"{base_url}/projects/itemeditor/storage/{command_uuid}"
return jsonify({
'success': True,
'uuid': command_uuid,
'url': retrieval_url,
'expires_at': expires_at.isoformat()
}), 201
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/projects/itemeditor/storage/<command_uuid>')
def retrieve_command(command_uuid):
"""Retrieve stored command by UUID (JSON response for plugin)"""
cleanup_expired_commands()
with storage_lock:
command_data = command_storage.get(command_uuid)
if not command_data:
return jsonify({'error': 'Command not found or expired'}), 404
# Check if expired
if datetime.fromisoformat(command_data['expires_at']) < datetime.now():
with storage_lock:
del command_storage[command_uuid]
return jsonify({'error': 'Command expired'}), 410
return jsonify(command_data), 200
# Serve /versions as JSON # Serve /versions as JSON
@app.route('/versions') @app.route('/versions')
def versions(): def versions():

View File

@@ -0,0 +1,509 @@
{% extends "base.html" %}
{% block title %}Item Editor - Command Storage - Devanturas{% endblock %}
{% block description %}Store commands longer than 256 characters temporarily for Minecraft Item Editor plugin{% endblock %}
{% block content %}
<section class="page-header">
<div class="container">
<div class="project-breadcrumb">
<a href="{{ url_for('projects') }}">Projects</a> / Item Editor / Command Storage
</div>
<h1>Item Editor Command Storage</h1>
<p>Store long commands (>256 chars) temporarily and generate a retrievable link</p>
</div>
</section>
<section class="command-storage-section">
<div class="container">
<div class="storage-grid">
<!-- Left: Command Input -->
<div class="storage-card">
<div class="card-header">
<i class="fas fa-terminal"></i>
<h2>Store Command</h2>
</div>
<p class="card-description">
Paste your Minecraft command below. Commands longer than 256 characters will be stored
for 30 minutes and a unique retrieval link will be generated.
</p>
<form id="commandForm">
<div class="form-group">
<label for="command">Minecraft Command</label>
<textarea
id="command"
name="command"
rows="6"
placeholder="Paste your command here..."
required
></textarea>
<div class="char-counter">
<span id="charCount">0</span> characters
<span id="charStatus"></span>
</div>
</div>
<button type="submit" class="btn btn-primary" id="submitBtn">
<i class="fas fa-save"></i> Store Command
</button>
</form>
<!-- Success Message -->
<div id="successMessage" style="display: none;" class="success-box">
<div class="success-header">
<i class="fas fa-check-circle"></i>
<h3>Command Stored Successfully!</h3>
</div>
<p class="success-description">Your command has been stored for 30 minutes.</p>
<div class="link-display">
<label>Retrieval Link (JSON):</label>
<div class="link-box">
<input type="text" id="generatedLink" readonly>
<button class="btn btn-secondary btn-copy" onclick="copyLink()">
<i class="fas fa-copy"></i> Copy
</button>
</div>
<small>Use this link in your Item Editor plugin to load the command.</small>
</div>
<div class="expiry-info">
<i class="fas fa-clock"></i>
<span>Expires in: <strong id="expiryTime">30 minutes</strong></span>
</div>
<button class="btn btn-outline" onclick="resetForm()">
<i class="fas fa-plus"></i> Store Another Command
</button>
</div>
</div>
<!-- Right: Info & Stats -->
<div class="info-sidebar">
<div class="info-card">
<div class="info-icon">
<i class="fas fa-info-circle"></i>
</div>
<h3>How It Works</h3>
<ol class="info-steps">
<li>Paste your long Minecraft command</li>
<li>Click "Store Command" to generate a link</li>
<li>Copy the generated JSON link</li>
<li>Use the link in your Item Editor plugin</li>
</ol>
</div>
<div class="info-card">
<div class="info-icon">
<i class="fas fa-shield-alt"></i>
</div>
<h3>Storage Limits</h3>
<ul class="info-list">
<li><i class="fas fa-check"></i> <strong>Duration:</strong> 30 minutes</li>
<li><i class="fas fa-check"></i> <strong>Max Length:</strong> 10,000 characters</li>
<li><i class="fas fa-check"></i> <strong>Format:</strong> JSON response</li>
<li><i class="fas fa-check"></i> <strong>Access:</strong> Anyone with link</li>
</ul>
</div>
<div class="info-card">
<div class="info-icon">
<i class="fas fa-code"></i>
</div>
<h3>Plugin Usage</h3>
<p class="code-description">Load command in your plugin:</p>
<pre class="code-block">GET https://devanturas.net/projects/itemeditor/storage/{uuid}
Response:
{
"command": "your command here",
"created_at": "2026-01-07T12:00:00",
"expires_at": "2026-01-07T12:30:00"
}</pre>
</div>
</div>
</div>
</div>
</section>
<style>
.page-header {
padding: 110px 0 40px;
background: #0f0f0f;
border-bottom: 1px solid #1f1f1f;
}
.command-storage-section {
padding: 60px 0;
background: #0a0a0a;
}
.storage-grid {
display: grid;
grid-template-columns: 1.5fr 1fr;
gap: 2rem;
align-items: start;
}
.storage-card, .info-card {
background: linear-gradient(145deg, #161616, #1e1e1e);
border: 1px solid #2a2a2a;
border-radius: 16px;
padding: 2rem;
}
.card-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.card-header i {
color: #00d4ff;
font-size: 2rem;
}
.card-header h2 {
color: #fff;
font-size: 1.8rem;
margin: 0;
}
.card-description {
color: #cfcfcf;
margin-bottom: 2rem;
line-height: 1.6;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
color: #00d4ff;
font-weight: 600;
margin-bottom: 0.5rem;
}
.form-group textarea {
width: 100%;
background: #121212;
border: 1px solid #2a2a2a;
border-radius: 10px;
padding: 1rem;
color: #fff;
font-family: 'Courier New', monospace;
font-size: 0.95rem;
resize: vertical;
transition: border-color 0.3s;
}
.form-group textarea:focus {
outline: none;
border-color: #00d4ff;
}
.char-counter {
display: flex;
justify-content: space-between;
margin-top: 0.5rem;
font-size: 0.9rem;
}
#charCount {
color: #00d4ff;
font-weight: 700;
}
#charStatus {
color: #999;
}
#charStatus.warning {
color: #ffaa00;
}
#charStatus.error {
color: #ff5555;
}
.success-box {
margin-top: 2rem;
padding: 2rem;
background: rgba(0, 255, 166, 0.05);
border: 1px solid rgba(0, 255, 166, 0.2);
border-radius: 12px;
}
.success-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.success-header i {
color: #00ffa6;
font-size: 2rem;
}
.success-header h3 {
color: #00ffa6;
margin: 0;
}
.success-description {
color: #cfcfcf;
margin-bottom: 1.5rem;
}
.link-display {
margin-bottom: 1.5rem;
}
.link-display label {
display: block;
color: #00d4ff;
font-weight: 600;
margin-bottom: 0.5rem;
}
.link-box {
display: flex;
gap: 0.5rem;
}
.link-box input {
flex: 1;
background: #121212;
border: 1px solid #2a2a2a;
border-radius: 8px;
padding: 0.75rem;
color: #00d4ff;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
}
.btn-copy {
padding: 0.75rem 1rem;
white-space: nowrap;
}
.link-display small {
display: block;
color: #999;
margin-top: 0.5rem;
}
.expiry-info {
display: flex;
align-items: center;
gap: 0.5rem;
color: #cfcfcf;
margin-bottom: 1.5rem;
}
.expiry-info i {
color: #ffaa00;
}
.info-sidebar {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.info-icon {
width: 50px;
height: 50px;
background: rgba(0, 212, 255, 0.1);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
.info-icon i {
color: #00d4ff;
font-size: 1.5rem;
}
.info-card h3 {
color: #fff;
margin-bottom: 1rem;
}
.info-steps {
padding-left: 1.5rem;
color: #cfcfcf;
line-height: 1.8;
}
.info-list {
list-style: none;
padding: 0;
}
.info-list li {
color: #cfcfcf;
margin-bottom: 0.75rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.info-list i {
color: #00ffa6;
font-size: 0.9rem;
}
.code-description {
color: #cfcfcf;
margin-bottom: 1rem;
}
.code-block {
background: #0a0a0a;
border: 1px solid #1f1f1f;
border-radius: 8px;
padding: 1rem;
color: #00d4ff;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
overflow-x: auto;
line-height: 1.6;
}
@media (max-width: 992px) {
.storage-grid {
grid-template-columns: 1fr;
}
}
</style>
<script>
const textarea = document.getElementById('command');
const charCount = document.getElementById('charCount');
const charStatus = document.getElementById('charStatus');
const commandForm = document.getElementById('commandForm');
const submitBtn = document.getElementById('submitBtn');
const successMessage = document.getElementById('successMessage');
// Character counter
textarea.addEventListener('input', function() {
const length = this.value.length;
charCount.textContent = length;
if (length === 0) {
charStatus.textContent = '';
charStatus.className = '';
} else if (length <= 256) {
charStatus.textContent = '(within chat limit)';
charStatus.className = '';
} else if (length <= 10000) {
charStatus.textContent = '(exceeds chat limit - storage required)';
charStatus.className = 'warning';
} else {
charStatus.textContent = '(exceeds maximum length)';
charStatus.className = 'error';
}
});
// Form submission
commandForm.addEventListener('submit', async function(e) {
e.preventDefault();
const command = textarea.value.trim();
if (!command) return;
if (command.length > 10000) {
alert('Command exceeds maximum length of 10,000 characters');
return;
}
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Storing...';
try {
const response = await fetch('/projects/itemeditor/storage', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ command: command })
});
const data = await response.json();
if (data.success) {
// Hide form, show success
commandForm.style.display = 'none';
successMessage.style.display = 'block';
// Set generated link
document.getElementById('generatedLink').value = data.url;
// Start countdown
startCountdown(data.expires_at);
} else {
alert('Error: ' + (data.error || 'Failed to store command'));
submitBtn.disabled = false;
submitBtn.innerHTML = '<i class="fas fa-save"></i> Store Command';
}
} catch (error) {
console.error('Error:', error);
alert('Failed to store command. Please try again.');
submitBtn.disabled = false;
submitBtn.innerHTML = '<i class="fas fa-save"></i> Store Command';
}
});
function copyLink() {
const linkInput = document.getElementById('generatedLink');
linkInput.select();
document.execCommand('copy');
const copyBtn = event.target.closest('.btn-copy');
const originalHTML = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check"></i> Copied!';
setTimeout(() => {
copyBtn.innerHTML = originalHTML;
}, 2000);
}
function resetForm() {
commandForm.style.display = 'block';
successMessage.style.display = 'none';
textarea.value = '';
charCount.textContent = '0';
charStatus.textContent = '';
submitBtn.disabled = false;
submitBtn.innerHTML = '<i class="fas fa-save"></i> Store Command';
}
function startCountdown(expiresAt) {
const expiryTime = document.getElementById('expiryTime');
const endTime = new Date(expiresAt).getTime();
const interval = setInterval(() => {
const now = new Date().getTime();
const distance = endTime - now;
if (distance < 0) {
clearInterval(interval);
expiryTime.innerHTML = '<span style="color: #ff5555;">Expired</span>';
return;
}
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
expiryTime.textContent = `${minutes}m ${seconds}s`;
}, 1000);
}
</script>
{% endblock %}

View File

@@ -24,8 +24,8 @@
"beta": "https://modrinth.com/plugin/velocity-friends/version/1.4.0-SNAPSHOT-Rev-4" "beta": "https://modrinth.com/plugin/velocity-friends/version/1.4.0-SNAPSHOT-Rev-4"
}, },
"links": { "links": {
"modrinth": "https://modrinth.com/plugin/velocity-friends/version/1.3.4", "modrinth": "https://modrinth.com/plugin/velocity-friends",
"hangar": "https://hangar.papermc.io/SimolZimol/Velocity-Friends/versions/1.3.4", "hangar": "https://hangar.papermc.io/SimolZimol/Velocity-Friends",
"Ko-fi": "https://ko-fi.com/simolzimol", "Ko-fi": "https://ko-fi.com/simolzimol",
"discord": "https://discord.com/invite/vVrpvBEfeQ" "discord": "https://discord.com/invite/vVrpvBEfeQ"
}, },
@@ -50,18 +50,51 @@
"Interactive settings menu" "Interactive settings menu"
], ],
"technologies": ["Java", "Velocity API", "Adventure API", "Gson", "MySQL", "HikariCP"], "technologies": ["Java", "Velocity API", "Adventure API", "Gson", "MySQL", "HikariCP"],
"commands": [ "commands": [{
{"command": "/friend add <player>", "description": "Send a friend request to a player.", "permission": "friends.use"}, "command": "/friend add <player>",
{"command": "/friend accept <player>", "description": "Accept a pending friend request.", "permission": "friends.use"}, "description": "Send a friend request to a player.",
{"command": "/friend deny <player>", "description": "Deny a friend request.", "permission": "friends.use"}, "permission": "friends.use"
{"command": "/friend remove <player>", "description": "Remove a friend from your list.", "permission": "friends.use"}, }, {
{"command": "/friend list", "description": "Show all your friends with online status and server location.", "permission": "friends.use"}, "command": "/friend accept <player>",
{"command": "/friend requests", "description": "Show all pending friend requests.", "permission": "friends.use"}, "description": "Accept a pending friend request.",
{"command": "/friend msg <player> <message>", "description": "Send a private message to a friend.", "permission": "friends.use"}, "permission": "friends.use"
{"command": "/friend reply <message>", "description": "Reply to the last received message.", "permission": "friends.use"}, }, {
{"command": "/friend broadcast <message>", "description": "Send a message to all online friends.", "permission": "friends.use"}, "command": "/friend deny <player>",
{"command": "/friend settings", "description": "Open the interactive settings menu.", "permission": "friends.use"}, "description": "Deny a friend request.",
{"command": "/friend reload", "description": "Reload the plugin configuration (admin only).", "permission": "friends.reload"} "permission": "friends.use"
}, {
"command": "/friend remove <player>",
"description": "Remove a friend from your list.",
"permission": "friends.use"
}, {
"command": "/friend list",
"description": "Show all your friends with online status and server location.",
"permission": "friends.use"
}, {
"command": "/friend requests",
"description": "Show all pending friend requests.",
"permission": "friends.use"
}, {
"command": "/friend msg <player> <message>",
"description": "Send a private message to a friend.",
"permission": "friends.use"
}, {
"command": "/friend reply <message>",
"description": "Reply to the last received message.",
"permission": "friends.use"
}, {
"command": "/friend broadcast <message>",
"description": "Send a message to all online friends.",
"permission": "friends.use"
}, {
"command": "/friend settings",
"description": "Open the interactive settings menu.",
"permission": "friends.use"
}, {
"command": "/friend reload",
"description": "Reload the plugin configuration (admin only).",
"permission": "friends.reload"
}
], ],
"installation": [ "installation": [
"Download the plugin jar from Modrinth or Spigot.", "Download the plugin jar from Modrinth or Spigot.",
@@ -130,10 +163,19 @@
"Hot-reload configuration support" "Hot-reload configuration support"
], ],
"technologies": ["Java", "Velocity API", "Adventure API", "Gson", "SnakeYAML"], "technologies": ["Java", "Velocity API", "Adventure API", "Gson", "SnakeYAML"],
"commands": [ "commands": [{
{"command": "/joinme", "description": "Share your current server location with players on configured servers.", "permission": "joinme.use"}, "command": "/joinme",
{"command": "/joinmereload", "description": "Reload the plugin configuration.", "permission": "joinme.reload"}, "description": "Share your current server location with players on configured servers.",
{"command": "/joinmeversion", "description": "Check the plugin version and available updates.", "permission": "joinme.use"} "permission": "joinme.use"
}, {
"command": "/joinmereload",
"description": "Reload the plugin configuration.",
"permission": "joinme.reload"
}, {
"command": "/joinmeversion",
"description": "Check the plugin version and available updates.",
"permission": "joinme.use"
}
], ],
"installation": [ "installation": [
"Download the plugin jar from GitHub releases.", "Download the plugin jar from GitHub releases.",
@@ -179,10 +221,10 @@
"stable": "https://modrinth.com/plugin/simpleteleports/version/1.2.1", "stable": "https://modrinth.com/plugin/simpleteleports/version/1.2.1",
"beta": "https://modrinth.com/plugin/simpleteleports/version/1.2.2" "beta": "https://modrinth.com/plugin/simpleteleports/version/1.2.2"
}, },
"links": { "links": {
"modrinth": "https://modrinth.com/plugin/simpleteleports/version/1.2.1", "modrinth": "https://modrinth.com/plugin/simpleteleports",
"hangar": "https://hangar.papermc.io/SimolZimol/SimpleTeleport/versions/1.2.1", "hangar": "https://hangar.papermc.io/SimolZimol/SimpleTeleport",
"spigot": "https://www.spigotmc.org/resources/simpleteleport.130115/update?update=622743", "spigot": "https://www.spigotmc.org/resources/simpleteleport.130115",
"Ko-fi": "https://ko-fi.com/simolzimol", "Ko-fi": "https://ko-fi.com/simolzimol",
"discord": "https://discord.com/invite/vVrpvBEfeQ" "discord": "https://discord.com/invite/vVrpvBEfeQ"
}, },
@@ -204,21 +246,63 @@
"Automatic version checking and update notifications" "Automatic version checking and update notifications"
], ],
"technologies": ["Java", "Spigot API", "Gson", "YAML"], "technologies": ["Java", "Spigot API", "Gson", "YAML"],
"commands": [ "commands": [{
{"command": "/sethome [name]", "description": "Set your personal home location (optionally with a name)", "permission": "SimpleTeleport.sethome"}, "command": "/sethome [name]",
{"command": "/home [name]", "description": "Teleport to your home (optionally specify a home name)", "permission": "SimpleTeleport.home"}, "description": "Set your personal home location (optionally with a name)",
{"command": "/homes", "description": "List all your homes (clickable/hoverable list)", "permission": "SimpleTeleport.home"}, "permission": "SimpleTeleport.sethome"
{"command": "/homeother <player> [home]", "description": "Teleport to another player's named home", "permission": "SimpleTeleport.home.others"}, }, {
{"command": "/delhome [name]", "description": "Delete one of your homes", "permission": "SimpleTeleport.delhome"}, "command": "/home [name]",
{"command": "/delhomeother <player> [home]", "description": "Delete another player's named home", "permission": "SimpleTeleport.delhome.others"}, "description": "Teleport to your home (optionally specify a home name)",
{"command": "/warp <name>", "description": "Teleport to a warp point", "permission": "SimpleTeleport.warp"}, "permission": "SimpleTeleport.home"
{"command": "/setwarp <name>", "description": "Create a new warp point", "permission": "SimpleTeleport.setwarp"}, }, {
{"command": "/delwarp <name>", "description": "Delete a warp point", "permission": "SimpleTeleport.delwarp"}, "command": "/homes",
{"command": "/warps", "description": "List all available warps", "permission": "SimpleTeleport.warps"}, "description": "List all your homes (clickable/hoverable list)",
{"command": "/spawn", "description": "Teleport to the server spawn", "permission": "SimpleTeleport.spawn"}, "permission": "SimpleTeleport.home"
{"command": "/spawn set", "description": "Set the server spawn location", "permission": "SimpleTeleport.setspawn"}, }, {
{"command": "/back", "description": "Teleport to your last location", "permission": "SimpleTeleport.back"}, "command": "/homeother <player> [home]",
{"command": "/stpversion", "description": "Display plugin version information and check for updates", "permission": "SimpleTeleport.version"} "description": "Teleport to another player's named home",
"permission": "SimpleTeleport.home.others"
}, {
"command": "/delhome [name]",
"description": "Delete one of your homes",
"permission": "SimpleTeleport.delhome"
}, {
"command": "/delhomeother <player> [home]",
"description": "Delete another player's named home",
"permission": "SimpleTeleport.delhome.others"
}, {
"command": "/warp <name>",
"description": "Teleport to a warp point",
"permission": "SimpleTeleport.warp"
}, {
"command": "/setwarp <name>",
"description": "Create a new warp point",
"permission": "SimpleTeleport.setwarp"
}, {
"command": "/delwarp <name>",
"description": "Delete a warp point",
"permission": "SimpleTeleport.delwarp"
}, {
"command": "/warps",
"description": "List all available warps",
"permission": "SimpleTeleport.warps"
}, {
"command": "/spawn",
"description": "Teleport to the server spawn",
"permission": "SimpleTeleport.spawn"
}, {
"command": "/spawn set",
"description": "Set the server spawn location",
"permission": "SimpleTeleport.setspawn"
}, {
"command": "/back",
"description": "Teleport to your last location",
"permission": "SimpleTeleport.back"
}, {
"command": "/stpversion",
"description": "Display plugin version information and check for updates",
"permission": "SimpleTeleport.version"
}
], ],
"installation": [ "installation": [
"Download the plugin jar from Modrinth.", "Download the plugin jar from Modrinth.",
@@ -234,7 +318,80 @@
"Full permission and configuration system" "Full permission and configuration system"
], ],
"stats": { "stats": {
"downloads": "500 +", "downloads": "over 500",
"likes": 0,
"followers": 0
}
},
"itemeditor": {
"stable": "1.2.0",
"beta": "1.2.1",
"dev": null,
"project_status": "stable",
"status": {
"stable": "stable",
"beta": "beta",
"dev": "unstable"
},
"version_id": {
"stable": "1.2.0",
"beta": "1.2.1"
},
"project_type": "spigot",
"name": "Item Editor",
"tagline": "Powerful in-game item editor for Spigot servers",
"description": "Edit Minecraft items in-game with a modern GUI, including lore, color, enchantments, PvP/No PvP, and custom actions.",
"long_description": "Item Editor is a comprehensive and user-friendly plugin for Spigot servers, allowing players and admins to edit items directly in-game. Features include a modern GUI for editing item name, lore, color, and enchantments, as well as per-item PvP/No PvP toggles and custom left-click actions. Messages and feedback are fully customizable, and all features are accessible via an intuitive menu.",
"icon": "fas fa-edit",
"download": {
"stable": "https://modrinth.com/plugin/itemeditor/version/1.2.0",
"beta": null
},
"links": {
"modrinth": "https://modrinth.com/plugin/itemeditor",
"hangar": "https://hangar.papermc.io/SimolZimol/Itemeditor",
"spigot": "https://www.spigotmc.org/resources/itemeditor.130589/",
"Ko-fi": "https://ko-fi.com/simolzimol",
"discord": "https://discord.com/invite/vVrpvBEfeQ"
},
"mc_compat": {
"stable": "1.19, 1.20, 1.21"
},
"server_types": ["Spigot", "Paper"],
"features": [
"Modern GUI for item editing",
"Edit name, lore, color, and enchantments",
"Per-item PvP and No PvP toggles",
"Custom left-click actions (3 modes)",
"Customizable and disable-able messages",
"Persistent item data storage",
"Color code and placeholder support"
],
"technologies": ["Java", "Spigot API", "Adventure API", "Gson"],
"commands": [{
"command": "/itemeditor",
"description": "Open the item editor GUI for the held item.",
"permission": "itemeditor.use"
}, {
"command": "/itemeditor reload",
"description": "Reload the plugin configuration.",
"permission": "itemeditor.reload"
}
],
"installation": [
"Download the plugin jar from the release page.",
"Place the jar in your Spigot/Paper plugins folder.",
"Restart the server.",
"Configure settings in config.yml and messages.yml as needed."
],
"technical_highlights": [
"Modern, intuitive GUI for all item features",
"Per-item persistent data using PersistentDataContainer",
"Color code-insensitive message disabling",
"Full permission and configuration system"
],
"stats": {
"downloads": "over 250",
"likes": 0, "likes": 0,
"followers": 0 "followers": 0
} }