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} +
+
+ Link: + ${cmd.url} +
+
+ 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 %}