modified: app.py
new file: templates/itemeditor_command_storage.html modified: versions/version.json
This commit is contained in:
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 %}
|
||||
Reference in New Issue
Block a user