1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Report a Bug - The Black Hole Bay</title> 7 <link rel="icon" type="image/jpeg" href="/images/icon-haloween.jpg"> 8 <style> 9 body { background-color: #050505; background-image: url('https://www.transparenttextures.com/patterns/stardust.png'); color: #e0e0e0; font-family: 'Segoe UI', sans-serif; margin: 0; padding: 40px 20px; } 10 .container { max-width: 600px; margin: 0 auto; background: #0d0d0d; border: 1px solid #1a1a1a; border-radius: 16px; padding: 30px; box-shadow: 0 8px 24px rgba(0,0,0,0.8); } 11 h1 { margin: 0 0 10px 0; font-size: 24px; color: #fff; } 12 p { color: #888; margin: 0 0 20px 0; font-size: 14px; } 13 .form-group { margin-bottom: 20px; } 14 label { display: block; margin-bottom: 8px; font-size: 13px; color: #94A3B8; font-weight: 600; } 15 textarea, input[type="text"] { width: 100%; padding: 12px; background: #050505; border: 1px solid #1a1a1a; border-radius: 8px; color: #fff; outline: none; font-family: inherit; box-sizing: border-box; } 16 textarea { min-height: 120px; resize: vertical; } 17 input[type="file"] { display: block; width: 100%; padding: 10px; background: #050505; border: 1px solid #1a1a1a; border-radius: 8px; color: #fff; box-sizing: border-box; } 18 .btn { display: inline-block; width: 100%; padding: 12px; background: #ff6b35; color: #fff; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: 0.2s; } 19 .btn:hover { background: #e55a25; } 20 .message { padding: 12px; border-radius: 8px; margin-top: 20px; text-align: center; display: none; } 21 .success { background: rgba(16, 185, 129, 0.1); color: #10B981; border: 1px solid rgba(16, 185, 129, 0.3); display: block; } 22 .error { background: rgba(244, 63, 94, 0.1); color: #F43F5E; border: 1px solid rgba(244, 63, 94, 0.3); display: block; } 23 .nav-back { display: inline-block; margin-top: 20px; color: #888; text-decoration: none; font-size: 14px; } 24 .nav-back:hover { color: #fff; } 25 </style> 26<link rel="stylesheet" href="/css/site-layout.css"></head> 27<body> 28 <div class="container"> 29 <div style="display:flex;gap:10px;margin-bottom:24px;background:#050505;padding:6px;border-radius:12px;border:1px solid #1a1a1a;"> 30 <div id="tab-bug" onclick="switchMode('bug')" style="flex:1;padding:10px;text-align:center;border-radius:8px;cursor:pointer;font-weight:600;font-size:14px;background:#ff6b35;color:#fff;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;vertical-align:-3px;"><path stroke-linecap="round" stroke-linejoin="round" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/></svg> Report a Bug</div> 31 <div id="tab-suggest" onclick="switchMode('suggest')" style="flex:1;padding:10px;text-align:center;border-radius:8px;cursor:pointer;font-weight:600;font-size:14px;background:transparent;color:#888;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:16px;height:16px;vertical-align:-3px;"><path stroke-linecap="round" stroke-linejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.488.76a2 2 0 01-1.576.897h-3.31a2 2 0 01-1.577-.897l-.488-.76z"/></svg> Suggest a Feature</div> 32 </div> 33 <div id="bug-pane"> 34 <h1>Report a Bug</h1> 35 <p>Found a broken link, a glitch, or something not working right? Let us know! You can attach a screenshot (images only).</p> 36 37 <form id="bug-form" onsubmit="submitBug(event)"> 38 <div class="form-group"> 39 <label for="description">Describe the bug:</label> 40 <textarea id="description" required placeholder="Please provide as much detail as possible (e.g., what you were doing, what you expected to happen, and what actually happened)."></textarea> 41 </div> 42 <div class="form-group"> 43 <label for="image">Attach Screenshot (Optional - Images Only):</label> 44 <input type="file" id="image" accept="image/png, image/jpeg, image/gif, image/webp"> 45 </div> 46 <button type="submit" class="btn" id="submit-btn">Submit Report</button> 47 </form> 48 </div> 49 50 <div id="suggest-pane" style="display:none;"> 51 <h1>Suggest a Feature</h1> 52 <p>Got an idea that would make Black Hole Bay better? This is the place! Big or small, we read every suggestion.</p> 53 <form id="suggestion-form" onsubmit="submitSuggestion(event)"> 54 <div class="form-group"> 55 <label for="s-title">Your idea in one line:</label> 56 <input type="text" id="s-title" maxlength="160" required placeholder="e.g., Add a watchlist for movies"> 57 </div> 58 <div class="form-group"> 59 <label for="s-category">Category:</label> 60 <select id="s-category" style="width:100%;padding:12px;background:#050505;border:1px solid #1a1a1a;border-radius:8px;color:#fff;outline:none;box-sizing:border-box;"> 61 <option value="general">General</option> 62 <option value="website">Website</option> 63 <option value="desktop">Desktop App</option> 64 <option value="streaming">Streaming / Player</option> 65 <option value="torrents">Torrents / Search</option> 66 <option value="community">Community</option> 67 </select> 68 </div> 69 <div class="form-group"> 70 <label for="s-description">Describe your idea:</label> 71 <textarea id="s-description" maxlength="2000" required placeholder="What would it do? Why would it be useful? Any details help."></textarea> 72 </div> 73 <input type="text" id="s-website" name="website" tabindex="-1" autocomplete="off" style="position:absolute;
73left:-9999px;" aria-hidden="true"> 74 <button type="submit" class="btn" id="suggest-btn">Submit Suggestion</button> 75 </form> 76 </div> 77 78 <div id="message" class="message"></div> 79 <a href="/" class="nav-back">← Back to Search</a> 80 </div> 81 82
82<script> 83 async function submitBug(e) { 84 e.preventDefault(); 85 const btn = document.getElementById('submit-btn'); 86 const msgBox = document.getElementById('message'); 87 const desc = document.getElementById('description').value; 88 const fileInput = document.getElementById('image'); 89 90 btn.disabled = true; 91 btn.textContent = 'Submitting...'; 92 msgBox.style.display = 'none'; 93 msgBox.className = 'message'; 94 95 let payload = { description: desc, image: null }; 96 97 if (fileInput.files.length > 0) { 98 const file = fileInput.files[0]; 99 100 // Strict client-side validation 101 if (!file.type.match(/^image\/(png|jpeg|jpg|gif|webp)$/)) { 102 msgBox.textContent = 'Error: Only image files (PNG, JPEG, GIF, WEBP) are allowed.'; 103 msgBox.classList.add('error'); 104 btn.disabled = false; 105 btn.textContent = 'Submit Report'; 106 return; 107 } 108 109 // Convert to Base64 110 const reader = new FileReader(); 111 reader.onload = async function(e) { 112 payload.image = e.target.result; 113 await sendRequest(payload, btn, msgBox); 114 }; 115 reader.onerror = function() { 116 msgBox.textContent = 'Error reading file.'; 117 msgBox.classList.add('error'); 118 btn.disabled = false; 119 btn.textContent = 'Submit Report'; 120 }; 121 reader.readAsDataURL(file); 122 } else { 123 await sendRequest(payload, btn, msgBox); 124 } 125 } 126 127 async function sendRequest(payload, btn, msgBox) { 128 try { 129 const res = await fetch('/api/bug-report', { 130 method: 'POST', 131 headers: { 'Content-Type': 'application/json' }, 132 body: JSON.stringify(payload) 133 }); 134 const data = await res.json(); 135 136 if (res.ok && data.success) { 137 msgBox.textContent = 'Bug report submitted successfully! Thank you.'; 138 msgBox.classList.add('success'); 139 document.getElementById('bug-form').reset(); 140 } else { 141 msgBox.textContent = data.error || 'Failed to submit report. Please try again.'; 142 msgBox.classList.add('error'); 143 } 144 } catch (err) { 145 msgBox.textContent = 'Network error. Please try again.'; 146 msgBox.classList.add('error'); 147 } finally { 148 btn.disabled = false; 149 btn.textContent = 'Submit Report'; 150 } 151 } 152 function switchMode(m) { 153 document.getElementById('tab-bug').style.background = m === 'bug' ? '#ff6b35' : 'transparent'; 154 document.getElementById('tab-bug').style.color = m === 'bug' ? '#fff' : '#888'; 155 document.getElementById('tab-suggest').style.background = m === 'suggest' ? '#ff6b35' : 'transparent'; 156 document.getElementById('tab-suggest').style.color = m === 'suggest' ? '#fff' : '#888'; 157 document.getElementById('bug-pane').style.display = m === 'bug' ? 'block' : 'none'; 158 document.getElementById('suggest-pane').style.display = m === 'suggest' ? 'block' : 'none'; 159 var msgBox = document.getElementById('message'); 160 msgBox.className = 'message'; msgBox.style.display = 'none'; msgBox.textContent = ''; 161 } 162 async function submitSuggestion(e) { 163 e.preventDefault(); 164 var btn = document.getElementById('suggest-btn'); 165 var msgBox = document.getElementById('message'); 166 var title = document.getElementById('s-title').value.trim(); 167 var description = document.getElementById('s-description').value.trim(); 168 var category = document.getElementById('s-category').value; 169 var website = document.getElementById('s-website').value; 170 if (title.length < 3) { msgBox.textContent = 'Please give your idea a title (3+ characters).'; msgBox.classList.add('error'); return; } 171 if (description.length < 10) { msgBox.textContent = 'Please describe your idea a bit more (10+ characters).'; msgBox.classList.add('error'); return; } 172 btn.disabled = true; btn.textContent = 'Submitting...'; 173 try { 174 var res = await fetch('/api/suggestions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: title, category: category, description: description, website: website }) }); 175 var data = await res.json(); 176 if (res.ok && data.success) { msgBox.textContent = data.message || 'Thanks! Your suggestion was submitted.'; msgBox.classList.add('success'); document.getElementById('suggestion-form').reset(); } 177 else { msgBox.textContent = data.error || 'Failed to submit. Please try again.'; msgBox.classList.add('error'); } 178 } catch (err) { msgBox.textContent = 'Network error. Please try again.'; msgBox.classList.add('error'); } 179 finally { btn.disabled = false; btn.textContent = 'Submit Suggestion'; } 180 } 181 if (location.hash === '#suggest') switchMode('suggest'); 182 </script>
vendor: 1 bytes, line 182
182
183<script defer src="/js/site-footer.js"></script>
183<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v4bc70e2c01a94c73b74392e4234840661791215815920" integrity="sha512-L0ha0OXavK/8okipN9F8BtP84dg9DUhPERbBXzwI6dgTA55d2+yweo3pn5CSFYs45/r8md2+xvUPtTdvTNRfjA==" data-cf-beacon='{"version":"2024.11.0","token":"fed1a331b87d4dc7b361c56f56c914c2","r":1,"spa":2}' crossorigin="anonymous"></script>
183 184</body> 185</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.