modified: app.py
new file: templates/itemeditor_command_storage.html modified: versions/version.json
This commit is contained in:
87
app.py
87
app.py
@@ -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():
|
||||||
|
|||||||
509
templates/itemeditor_command_storage.html
Normal file
509
templates/itemeditor_command_storage.html
Normal 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 %}
|
||||||
@@ -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.",
|
||||||
@@ -180,9 +222,9 @@
|
|||||||
"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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user