diff --git a/templates/itemeditor_command_storage.html b/templates/itemeditor_command_storage.html
index 93c8409..e4e0895 100644
--- a/templates/itemeditor_command_storage.html
+++ b/templates/itemeditor_command_storage.html
@@ -77,6 +77,12 @@
Store Another Command
+
+
+
+
Recently Stored Commands
+
+
@@ -436,6 +442,51 @@ if (isset($_GET['id'])) {
line-height: 1.6;
}
+.recent-commands-section {
+ background: #181818;
+ border:1px solid #232323;
+ border-radius: 14px;
+ padding: 1.5rem;
+ margin-top: 2.5rem;
+}
+.recent-commands-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1.2rem;
+}
+.recent-command-card {
+ background: #222;
+ border-radius: 10px;
+ padding: 1rem 1.2rem;
+}
+.recent-command-row {
+ display: flex;
+ gap: .7rem;
+ align-items: center;
+ margin-bottom: .3rem;
+}
+.recent-command-label {
+ color: #00d4ff;
+ font-size: .95rem;
+ min-width: 80px;
+}
+.recent-command-value {
+ color: #fff;
+ font-size: .97rem;
+}
+.recent-command-link {
+ color: #00ffa6;
+ font-size: .97rem;
+ word-break: break-all;
+}
+.recent-command-timer {
+ color: #ffaa00;
+ font-size: .97rem;
+}
+.recent-command-text {
+ font-family: 'Courier New', monospace;
+}
+
@media (max-width: 992px) {
.storage-grid {
grid-template-columns: 1fr;
@@ -541,6 +592,8 @@ function resetForm() {
charStatus.textContent = '';
submitBtn.disabled = false;
submitBtn.innerHTML = ' Store Command';
+ document.getElementById('generatedLink').value = '';
+ document.getElementById('expiryTime').textContent = '30 minutes';
}
function startCountdown(expiresAt) {
@@ -573,5 +626,112 @@ function startCountdown(expiresAt) {
}
}, 1000);
}
+
+// --- Recent Commands Storage (localStorage) ---
+function getRecentCommands() {
+ let cmds = [];
+ try {
+ cmds = JSON.parse(localStorage.getItem('recentCommands') || '[]');
+ } catch {}
+ return Array.isArray(cmds) ? cmds : [];
+}
+function setRecentCommands(cmds) {
+ localStorage.setItem('recentCommands', JSON.stringify(cmds));
+}
+function addRecentCommand(cmd) {
+ let cmds = getRecentCommands();
+ cmds.unshift(cmd);
+ cmds = cmds.filter(c => c && c.expires_at && new Date(c.expires_at).getTime() > Date.now());
+ if (cmds.length > 5) cmds = cmds.slice(0,5);
+ setRecentCommands(cmds);
+}
+function renderRecentCommands() {
+ const list = document.getElementById('recentCommandsList');
+ const cmds = getRecentCommands();
+ if (!cmds.length) {
+ list.innerHTML = 'No recent commands.
';
+ return;
+ }
+ list.innerHTML = '';
+ cmds.forEach((cmd, idx) => {
+ const div = document.createElement('div');
+ div.className = 'recent-command-card';
+ div.innerHTML = `
+
+ Command:
+ ${cmd.command.length > 60 ? cmd.command.slice(0,60)+'...' : cmd.command}
+
+
+
+ Expires in:
+
+
+ `;
+ list.appendChild(div);
+ startRecentCountdown(cmd.expires_at, `recent-timer-${idx}`, cmd.url);
+ });
+}
+function startRecentCountdown(expiresAt, elemId, url) {
+ const el = document.getElementById(elemId);
+ function update() {
+ const now = Date.now();
+ const end = new Date(expiresAt).getTime();
+ const dist = end - now;
+ if (dist < 0) {
+ el.innerHTML = 'Expired';
+ // Remove from localStorage
+ let cmds = getRecentCommands().filter(c => c.url !== url);
+ setRecentCommands(cmds);
+ renderRecentCommands();
+ return false;
+ }
+ const m = Math.floor((dist % (1000*60*60))/(1000*60));
+ const s = Math.floor((dist % (1000*60))/1000);
+ el.textContent = `${m}m ${s}s`;
+ return true;
+ }
+ if (!update()) return;
+ const interval = setInterval(() => {
+ if (!update()) clearInterval(interval);
+ }, 1000);
+}
+
+// On page load
+renderRecentCommands();
+
+// On successful store, add to recent
+const origSuccessHandler = function(data) {
+ addRecentCommand({
+ command: textarea.value.trim(),
+ url: data.url,
+ expires_at: data.expires_at
+ });
+ renderRecentCommands();
+};
+
+// Patch the form submit handler to call origSuccessHandler
+const origSubmit = commandForm.onsubmit;
+commandForm.onsubmit = function(e) {
+ if (origSubmit) origSubmit.call(this, e);
+};
+
+// Patch the fetch success in the submit handler
+const origFetch = window.fetch;
+window.fetch = async function() {
+ const res = await origFetch.apply(this, arguments);
+ if (arguments[0] && arguments[0].toString().includes('/projects/itemeditor/storage')) {
+ try {
+ const clone = res.clone();
+ const data = await clone.json();
+ if (data.success && textarea && textarea.value) {
+ origSuccessHandler(data);
+ }
+ } catch {}
+ }
+ return res;
+};
{% endblock %}