Files
GoNetKit/web/pwpush.html

1071 lines
29 KiB
HTML
Raw Normal View History

2025-07-17 21:52:52 +01:00
{{template "base.html" .}}
{{define "title"}}Password Pusher - Secure Text Sharing{{end}}
{{define "head"}}
{{if .Success}}
<meta name="success-data" content='{"id":"{{.ID}}","url":"{{.PushURL}}","expiresAt":"{{.ExpiresAt}}"}'>
{{end}}
<style>
:root {
--bg-color: #1e1e1e;
--text-color: #e0e0e0;
--section-bg: #2d2d2d;
--border-color: #404040;
--highlight: #3c5c7c;
--success: #4caf50;
--warning: #ff9800;
--error: #f44336;
--danger: #f44336;
--info: #2196f3;
--muted-color: #999;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
.container {
background-color: var(--bg-color);
color: var(--text-color);
}
input, textarea {
background-color: var(--section-bg);
color: var(--text-color);
border: 1px solid var(--border-color);
}
.push-form, .info-section, .history-section {
background-color: var(--section-bg);
border: 1px solid var(--border-color);
}
.history-header {
background-color: #333;
border-bottom: 1px solid var(--border-color);
}
.history-stats {
background-color: #333;
border-bottom: 1px solid var(--border-color);
}
.stat-badge {
background-color: var(--section-bg);
border: 1px solid var(--border-color);
color: var(--text-color);
}
.history-table th {
background-color: #333;
color: var(--text-color);
border-bottom: 2px solid var(--border-color);
}
.history-table td {
border-bottom: 1px solid var(--border-color);
color: var(--text-color);
}
.history-table tr:hover:not(.empty-row) {
background-color: #333;
}
.preview-text {
background-color: #333;
color: var(--text-color);
}
.btn-danger {
background: var(--error);
}
.btn-sm {
font-size: 12px;
padding: 6px 12px;
}
.notes-cell {
max-width: 120px;
font-size: 12px;
}
.notes-text {
color: #aaa;
font-style: italic;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.alert {
padding: 20px;
margin: 20px 0;
border-radius: 8px;
border-left: 4px solid var(--success);
}
.alert-success {
background-color: var(--section-bg);
color: var(--success);
border-color: var(--success);
}
.link-container {
display: flex;
gap: 10px;
margin: 15px 0;
}
.link-container input {
flex: 1;
padding: 10px;
border: 2px solid var(--border-color);
border-radius: 4px;
font-family: monospace;
background: var(--section-bg);
color: var(--text-color);
}
.copy-btn {
padding: 10px 15px;
background: var(--highlight);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.push-form {
background: var(--section-bg);
padding: 30px;
border-radius: 8px;
margin: 20px 0;
border: 1px solid var(--border-color);
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: var(--text-color);
}
.form-group textarea {
width: 100%;
min-height: 120px;
padding: 12px;
border: 2px solid var(--border-color);
border-radius: 4px;
font-family: monospace;
resize: vertical;
background: var(--section-bg);
color: var(--text-color);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-group input[type="range"] {
width: 100%;
margin: 10px 0;
}
.range-display {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 5px;
}
.range-display span {
font-weight: bold;
color: var(--highlight);
}
.checkbox-group {
display: flex;
flex-direction: column;
gap: 15px;
}
.checkbox-label {
display: flex;
align-items: flex-start;
gap: 10px;
cursor: pointer;
}
.checkbox-label input[type="checkbox"] {
margin-top: 2px;
}
.checkbox-label small {
display: block;
color: #aaa;
margin-top: 4px;
}
.btn {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
font-weight: bold;
transition: all 0.3s ease;
}
.btn-primary {
background: var(--highlight);
color: white;
}
.btn-primary:hover {
background: #2a4c6c;
}
.btn-secondary {
background: #6c757d;
color: white;
}
.btn-large {
padding: 15px 30px;
font-size: 18px;
width: 100%;
}
.info-section {
background: var(--section-bg);
padding: 25px;
border-radius: 8px;
margin: 20px 0;
border: 1px solid var(--border-color);
}
.info-section h3 {
margin-top: 0;
color: var(--text-color);
}
.info-section ul {
list-style: none;
padding: 0;
}
.info-section li {
padding: 8px 0;
border-bottom: 1px solid var(--border-color);
color: var(--text-color);
}
.info-section li:last-child {
border-bottom: none;
}
.actions {
margin-top: 20px;
text-align: center;
}
/* History Section Styles */
.history-section {
margin-top: 40px;
background: var(--section-bg);
border-radius: 8px;
border: 1px solid var(--border-color);
overflow: hidden;
}
.history-header {
background: var(--bg-color);
padding: 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.history-header h2 {
margin: 0;
color: var(--text-color);
}
.history-controls {
display: flex;
gap: 10px;
}
.history-stats {
padding: 15px 20px;
background: var(--bg-color);
border-bottom: 1px solid var(--border-color);
display: flex;
gap: 15px;
}
.stat-badge {
background: var(--section-bg);
padding: 6px 12px;
border-radius: 4px;
border: 1px solid var(--border-color);
font-size: 14px;
font-weight: bold;
color: var(--text-color);
}
.stat-badge.active {
background: var(--success);
color: white;
border-color: var(--success);
}
.stat-badge.expired {
background: var(--danger);
color: white;
border-color: var(--danger);
}
.history-table-container {
overflow-x: auto;
max-width: 100%;
}
.history-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
min-width: 600px;
}
.history-table th {
background: var(--bg-color);
padding: 10px 6px;
text-align: left;
font-weight: bold;
color: var(--text-color);
border-bottom: 2px solid var(--border-color);
white-space: nowrap;
font-size: 12px;
}
.history-table td {
padding: 10px 6px;
border-bottom: 1px solid var(--border-color);
vertical-align: middle;
color: var(--text-color);
font-size: 12px;
}
.history-table tr.expired {
background: var(--bg-color);
opacity: 0.7;
}
.history-table tr:hover:not(.empty-row) {
background: var(--section-bg);
}
.empty-row td {
text-align: center;
padding: 40px 20px;
color: var(--muted-color);
font-style: italic;
}
.date-cell {
white-space: nowrap;
color: var(--muted-color);
min-width: 110px;
font-size: 11px;
}
.views-cell {
text-align: center;
font-weight: bold;
min-width: 60px;
font-size: 11px;
}
.notes-cell {
max-width: 150px;
font-size: 11px;
word-break: break-word;
}
.status-cell {
text-align: center;
min-width: 80px;
}
.status-badge {
padding: 3px 6px;
border-radius: 4px;
font-size: 10px;
font-weight: bold;
white-space: nowrap;
}
.status-badge.active {
background: var(--success);
color: white;
}
.status-badge.expired {
background: var(--danger);
color: white;
}
.status-badge.deleted {
background: #6c757d;
color: white;
}
.actions-cell {
text-align: center;
white-space: nowrap;
min-width: 140px;
}
.btn-xs {
padding: 3px 6px;
font-size: 10px;
margin: 0 1px;
border-radius: 3px;
}
.expired-text {
color: var(--muted-color);
font-size: 12px;
font-style: italic;
}
@media (max-width: 768px) {
.form-row {
grid-template-columns: 1fr;
}
.link-container {
flex-direction: column;
}
.history-header {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.history-stats {
flex-wrap: wrap;
gap: 10px;
}
.history-table {
font-size: 10px;
min-width: 500px;
}
.history-table th,
.history-table td {
padding: 6px 3px;
}
.notes-cell {
max-width: 100px;
}
.date-cell {
font-size: 9px;
min-width: 90px;
}
.actions-cell {
min-width: 120px;
}
.btn-xs {
padding: 2px 4px;
font-size: 9px;
}
}
</style>
{{end}}
{{define "content"}}
<div class="container">
<h1>🔐 Password Pusher</h1>
<p>Share sensitive text securely with automatic expiration and view limits.</p>
{{if .Success}}
<div class="alert alert-success">
<h3>✅ Secure Link Created!</h3>
<p>Your text has been encrypted and stored securely. Share this link:</p>
<div class="link-container">
<input type="text" id="pushUrl" value="{{.PushURL}}" readonly onclick="this.select()">
<button onclick="copyToClipboard()" class="copy-btn">📋 Copy</button>
</div>
<p><small>🕒 Expires: {{.ExpiresAt}} | 🆔 ID: {{.ID}}</small></p>
<p><strong>⚠️ Important:</strong> This link will only work once if auto-delete is enabled!</p>
<a href="/pwpush" class="btn btn-primary">Create Another Link</a>
</div>
{{else}}
<form id="pushForm" method="post" class="push-form">
{{if .CSRFToken}}
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
{{end}}
<div class="form-group">
<label for="text">📝 Text to Share:</label>
<textarea name="text" id="text" placeholder="Enter your password, secret, or sensitive text here..." required></textarea>
<small>Enter any sensitive information you want to share securely.</small>
</div>
<div class="form-group">
<label for="protection_password">🔑 Password Protection (Optional):</label>
<input type="password" name="password" id="protection_password"
placeholder="Leave empty for no password protection"
style="width: 75%; padding: 15px 20px; border: 2px solid var(--border-color);
border-radius: 8px; background-color: var(--section-bg); color: var(--text-color);
font-size: 13px; font-family: monospace; margin-top: 5px;"><br>
<small>If set, viewers must enter this password to access the content. This bypasses the click-to-reveal feature.</small>
</div>
<div class="form-row">
<div class="form-group">
<label for="expiry_days">⏰ Expires After:</label>
<input type="range" name="expiry_days" id="expiry_days" min="1" max="90" value="7"
oninput="updateExpiryDisplay(this.value)">
<div class="range-display">
<span id="expiryDisplay">7 days</span>
<small>Max: 3 months</small>
</div>
</div>
<div class="form-group">
<label for="max_views">👁️ Maximum Views:</label>
<input type="range" name="max_views" id="max_views" min="1" max="100" value="10"
oninput="updateViewsDisplay(this.value)">
<div class="range-display">
<span id="viewsDisplay">10 views</span>
<small>Max: 100 views</small>
</div>
</div>
</div>
<div class="form-group">
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" name="require_click" checked>
<span class="checkmark"></span>
🛡️ Require click to reveal (recommended)
<small>Hides content from web crawlers and requires user interaction</small>
</label>
<label class="checkbox-label">
<input type="checkbox" name="auto_delete">
<span class="checkmark"></span>
🗑️ Allow manual deletion after viewing
<small>Adds a delete button when content is viewed (viewer can choose to delete)</small>
</label>
<label class="checkbox-label">
<input type="checkbox" name="track_history" id="track_history">
<span class="checkmark"></span>
📚 Save to my history
<small>Keep a record of your created links (stored in browser)</small>
</label>
</div>
</div>
<button type="submit" class="btn btn-primary btn-large">
🔒 Create Secure Link
</button>
</form>
<div class="info-section">
<h3>🔒 How it works:</h3>
<ul>
<li><strong>Encryption:</strong> Your text is encrypted before storage</li>
<li><strong>Automatic Expiry:</strong> Links expire after set time or view limit</li>
<li><strong>One-time Use:</strong> Optional auto-delete after viewing</li>
<li><strong>No Registration:</strong> No account required</li>
<li><strong>Browser History:</strong> Optionally track your links locally</li>
</ul>
</div>
{{end}}
<!-- History Section -->
<div class="history-section">
<div class="history-header">
<h2>📚 My Recent Links</h2>
<div class="history-controls">
<button onclick="refreshHistory()" class="btn btn-secondary btn-sm">🔄 Refresh</button>
<button onclick="clearHistory()" class="btn btn-danger btn-sm">🗑️ Clear All</button>
</div>
</div>
<div id="historyContainer">
<div class="history-stats">
<span class="stat-badge">Total: <span id="totalCount">0</span></span>
<span class="stat-badge active">Active: <span id="activeCount">0</span></span>
<span class="stat-badge expired">Expired: <span id="expiredCount">0</span></span>
</div>
<div class="history-table-container">
<table id="historyTable" class="history-table">
<thead>
<tr>
<th>📅 Created</th>
<th>⏰ Expires</th>
<th><EFBFBD> Views</th>
<th>📝 Local Notes</th>
<th>🔗 Status</th>
<th>⚡ Actions</th>
</tr>
</thead>
<tbody id="historyTableBody">
<tr class="empty-row">
<td colspan="6" class="empty-message">
📭 No history found. Enable "Save to my history" when creating links.
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
function updateExpiryDisplay(days) {
const display = document.getElementById('expiryDisplay');
if (days == 1) {
display.textContent = '1 day';
} else if (days <= 7) {
display.textContent = days + ' days';
} else if (days <= 30) {
const weeks = Math.floor(days / 7);
const remainingDays = days % 7;
if (remainingDays === 0) {
display.textContent = weeks + (weeks === 1 ? ' week' : ' weeks');
} else {
display.textContent = weeks + 'w ' + remainingDays + 'd';
}
} else {
const months = Math.floor(days / 30);
const remainingDays = days % 30;
if (remainingDays === 0) {
display.textContent = months + (months === 1 ? ' month' : ' months');
} else {
display.textContent = months + 'm ' + remainingDays + 'd';
}
}
}
function updateViewsDisplay(views) {
const display = document.getElementById('viewsDisplay');
display.textContent = views + (views === '1' ? ' view' : ' views');
}
function copyToClipboard() {
const urlInput = document.getElementById('pushUrl');
urlInput.select();
document.execCommand('copy');
const btn = document.querySelector('.copy-btn');
const originalText = btn.textContent;
btn.textContent = '✅ Copied!';
btn.style.background = '#4caf50';
setTimeout(() => {
btn.textContent = originalText;
btn.style.background = '';
}, 2000);
}
// Save settings to cookies when form is submitted
document.getElementById('pushForm').addEventListener('submit', function() {
const expiryDays = document.getElementById('expiry_days').value;
const maxViews = document.getElementById('max_views').value;
const requireClick = document.querySelector('input[name="require_click"]').checked;
const autoDelete = document.querySelector('input[name="auto_delete"]').checked;
const trackHistory = document.querySelector('input[name="track_history"]').checked;
setCookie('pwpush_expiry_days', expiryDays, 30);
setCookie('pwpush_max_views', maxViews, 30);
setCookie('pwpush_require_click', requireClick, 30);
setCookie('pwpush_auto_delete', autoDelete, 30);
setCookie('pwpush_track_history', trackHistory, 30);
});
// History functionality
function refreshHistory() {
// Show loading state
const refreshBtn = document.querySelector('button[onclick="refreshHistory()"]');
const originalText = refreshBtn.textContent;
refreshBtn.textContent = '🔄 Validating...';
refreshBtn.disabled = true;
setTimeout(() => {
loadHistory();
refreshBtn.textContent = originalText;
refreshBtn.disabled = false;
}, 100);
}
function validateLinksWithServer(history) {
// Only validate links that are not already marked as expired/deleted
const activeLinks = history.filter(item => !item.isDeleted && new Date(item.expiresAt) > new Date());
if (activeLinks.length === 0) {
return Promise.resolve(history); // No active links to validate
}
const ids = activeLinks.map(item => item.id);
return fetch('/pwpush/api/status/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ ids: ids })
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to validate links');
}
return response.json();
})
.then(statuses => {
// Update history items with server status
const statusMap = {};
statuses.forEach(status => {
statusMap[status.id] = status;
});
return history.map(item => {
const serverStatus = statusMap[item.id];
if (serverStatus) {
// Update with server data
item.exists = serverStatus.exists;
item.isDeleted = serverStatus.is_deleted;
item.viewCount = serverStatus.current_views;
item.maxViews = serverStatus.max_views;
// Mark as expired if server says so
if (serverStatus.is_expired || !serverStatus.exists) {
item.isExpired = true;
}
// If link doesn't exist on server, mark for removal
if (!serverStatus.exists) {
item.shouldRemove = true;
}
}
return item;
}).filter(item => !item.shouldRemove); // Remove non-existent links
})
.catch(error => {
console.warn('Failed to validate links with server:', error);
return history; // Return original history if validation fails
});
}
function loadHistory() {
let history = getHistoryFromCookie();
// Only validate if "track history" is enabled
const trackHistory = getCookie('pwpush_track_history');
if (trackHistory === 'true' && history.length > 0) {
// Validate with server and update display
validateLinksWithServer(history).then(validatedHistory => {
// Save updated history back to cookies
if (validatedHistory.length !== history.length) {
saveHistoryToCookie(validatedHistory);
history = validatedHistory;
}
displayHistory(history);
});
} else {
displayHistory(history);
}
}
function displayHistory(history) {
const tbody = document.getElementById('historyTableBody');
const emptyRow = tbody.querySelector('.empty-row');
if (history.length === 0) {
if (emptyRow) emptyRow.style.display = '';
updateStats(0, 0, 0);
return;
}
if (emptyRow) emptyRow.style.display = 'none';
// Clear existing rows except empty row
const existingRows = tbody.querySelectorAll('tr:not(.empty-row)');
existingRows.forEach(row => row.remove());
let activeCount = 0;
let expiredCount = 0;
history.forEach(item => {
const isExpired = item.isExpired || item.isDeleted || new Date(item.expiresAt) < new Date();
if (isExpired) expiredCount++;
else activeCount++;
const row = createHistoryRow(item, isExpired);
tbody.appendChild(row);
});
updateStats(history.length, activeCount, expiredCount);
}
function createHistoryRow(item, isExpired) {
const row = document.createElement('tr');
row.className = isExpired ? 'expired' : 'active';
// Determine status text and actions based on server state
let statusText = '✅ Active';
let statusClass = 'active';
let actions = '';
if (item.isDeleted) {
statusText = '🗑️ Deleted';
statusClass = 'deleted';
actions = '<span class="expired-text">Deleted on server</span>';
} else if (isExpired) {
statusText = '❌ Expired';
statusClass = 'expired';
actions = '<span class="expired-text">Expired</span>';
} else {
actions = `<button onclick="copyLink('${item.url}')" class="btn btn-secondary btn-xs" title="Copy Link">📋</button>
<a href="${item.url}" class="btn btn-primary btn-xs" target="_blank" title="View">👁️</a>
<button onclick="editNotes('${item.id}')" class="btn btn-secondary btn-xs" title="Edit Notes">✏️</button>`;
}
// Always show remove button for cleaning up history
actions += `<button onclick="removeFromHistory('${item.id}')" class="btn btn-danger btn-xs" title="Remove">🗑️</button>`;
row.innerHTML = `
<td class="date-cell">
${formatDate(item.createdAt)}
</td>
<td class="date-cell">
${formatDate(item.expiresAt)}
</td>
<td class="views-cell">
${item.viewCount || 0}/${item.maxViews}
</td>
<td class="notes-cell">
<span class="notes-text">${item.notes ? item.notes.substring(0, 30) + (item.notes.length > 30 ? '...' : '') : '-'}</span>
</td>
<td class="status-cell">
<span class="status-badge ${statusClass}">
${statusText}
</span>
</td>
<td class="actions-cell">
${actions}
</td>
`;
return row;
}
function updateStats(total, active, expired) {
document.getElementById('totalCount').textContent = total;
document.getElementById('activeCount').textContent = active;
document.getElementById('expiredCount').textContent = expired;
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
}
function copyLink(url) {
const textArea = document.createElement('textarea');
textArea.value = url;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
// Show success feedback
const btn = event.target;
const originalText = btn.textContent;
btn.textContent = '✅';
btn.style.background = '#4caf50';
setTimeout(() => {
btn.textContent = originalText;
btn.style.background = '';
}, 1500);
}
function removeFromHistory(pushId) {
if (confirm('Remove this item from your history?')) {
const history = getHistoryFromCookie();
const updatedHistory = history.filter(h => h.id !== pushId);
saveHistoryToCookie(updatedHistory);
loadHistory();
}
}
function editNotes(pushId) {
const history = getHistoryFromCookie();
const item = history.find(h => h.id === pushId);
if (!item) return;
const currentNotes = item.notes || '';
const newNotes = prompt('Add notes for this link (for your reference only):', currentNotes);
if (newNotes !== null) { // User didn't cancel
item.notes = newNotes;
saveHistoryToCookie(history);
loadHistory();
}
}
function clearHistory() {
if (confirm('Clear all history? This cannot be undone.')) {
// Show loading state
const clearBtn = document.querySelector('button[onclick="clearHistory()"]');
const originalText = clearBtn.textContent;
clearBtn.textContent = '🗑️ Clearing...';
clearBtn.disabled = true;
// Clear the cookie
document.cookie = "pwpush_history=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
// Refresh the display and restore button
setTimeout(() => {
loadHistory(); // Refresh the display to show cleared state
clearBtn.textContent = '✅ Cleared!';
// Restore original text after showing success
setTimeout(() => {
clearBtn.textContent = originalText;
clearBtn.disabled = false;
}, 1500);
}, 100);
}
}
function getHistoryFromCookie() {
const cookie = document.cookie
.split('; ')
.find(row => row.startsWith('pwpush_history='));
if (!cookie) return [];
try {
return JSON.parse(decodeURIComponent(cookie.split('=')[1]));
} catch (e) {
return [];
}
}
function saveHistoryToCookie(history) {
const expires = new Date();
expires.setTime(expires.getTime() + (30 * 24 * 60 * 60 * 1000)); // 30 days
document.cookie = `pwpush_history=${encodeURIComponent(JSON.stringify(history))};expires=${expires.toUTCString()};path=/`;
}
function getCookie(name) {
const value = "; " + document.cookie;
const parts = value.split("; " + name + "=");
if (parts.length === 2) return parts.pop().split(";").shift();
return null;
}
function setCookie(name, value, days) {
const expires = new Date();
expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = name + "=" + value + ";expires=" + expires.toUTCString() + ";path=/";
}
// Load history on page load
document.addEventListener('DOMContentLoaded', function() {
loadHistory();
// Check if this is a success page with a new link created
const successData = document.querySelector('meta[name="success-data"]');
if (successData) {
const trackHistory = getCookie('pwpush_track_history');
if (trackHistory === 'true') {
// Add the new link to history
const data = JSON.parse(successData.content);
const newItem = {
id: data.id,
url: data.url,
createdAt: new Date().toISOString(),
expiresAt: data.expiresAt,
maxViews: parseInt(getCookie('pwpush_max_views')) || 10,
viewCount: 0,
previewText: 'Content hidden for security'
};
let history = getHistoryFromCookie();
history.unshift(newItem); // Add to beginning
// Keep only last 50 items
if (history.length > 50) {
history = history.slice(0, 50);
}
saveHistoryToCookie(history);
loadHistory(); // Refresh the display
}
}
// Load saved preferences
const savedExpiry = getCookie('pwpush_expiry_days');
const savedViews = getCookie('pwpush_max_views');
const savedRequireClick = getCookie('pwpush_require_click');
const savedAutoDelete = getCookie('pwpush_auto_delete');
const savedTrackHistory = getCookie('pwpush_track_history');
if (savedExpiry) {
document.getElementById('expiry_days').value = savedExpiry;
updateExpiryDisplay(savedExpiry);
}
if (savedViews) {
document.getElementById('max_views').value = savedViews;
updateViewsDisplay(savedViews);
}
if (savedRequireClick !== null) {
document.querySelector('input[name="require_click"]').checked = savedRequireClick === 'true';
}
if (savedAutoDelete !== null) {
document.querySelector('input[name="auto_delete"]').checked = savedAutoDelete === 'true';
}
if (savedTrackHistory !== null) {
document.querySelector('input[name="track_history"]').checked = savedTrackHistory === 'true';
}
});
</script>
{{end}}