Rewild Your Web
18
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 346 lines 8.7 kB view raw
1<html> 2 <head> 3<style> 4 body { 5 font-family: monospace; 6 margin: 0; 7 padding: 0; 8 background: #fff; 9 color: #333; 10 } 11 12 #json-raw { 13 display: none; 14 } 15 16 #viewer { 17 padding: 0.5em 1em; 18 line-height: 1.5; 19 } 20 21 #toolbar { 22 display: flex; 23 gap: 1em; 24 padding: 0.5em; 25 align-items: center; 26 background: #f5f5f5; 27 border-bottom: 1px solid #ddd; 28 } 29 30 #toolbar button { 31 min-width: 5em; 32 } 33 34 #toolbar button.active { 35 background: #ddd; 36 font-weight: bold; 37 } 38 39 #raw-view { 40 display: none; 41 padding: 0.5em 1em; 42 white-space: pre-wrap; 43 word-break: break-all; 44 } 45 46 #smart-view { 47 display: none; 48 padding: 0.5em 1em; 49 } 50 51 .json-error { 52 padding: 0.5em 1em; 53 color: #c00; 54 font-weight: bold; 55 } 56 57 /* Syntax highlighting for Json data types */ 58 .json-key { 59 color: #881391; 60 } 61 62 .json-string { 63 color: #1a1aa6; 64 } 65 66 .json-number { 67 color: #1c00cf; 68 } 69 70 .json-boolean { 71 color: #0d22aa; 72 } 73 74 .json-null { 75 color: #808080; 76 } 77 78 /* Collapsible tree */ 79 .toggle { 80 cursor: pointer; 81 user-select: none; 82 } 83 84 .toggle::before { 85 content: "\25BC"; 86 display: inline-block; 87 width: 1em; 88 transition: transform 0.1s; 89 } 90 91 .toggle.collapsed::before { 92 transform: rotate(-90deg); 93 } 94 95 .collapsible { 96 margin-left: 1.5em; 97 } 98 99 .collapsible.hidden { 100 display: none; 101 } 102 103 .bracket { 104 color: #333; 105 } 106 107 .comma { 108 color: #333; 109 } 110 111 .line { 112 padding-left: 0; 113 } 114</style> 115<script> 116 // Shortcut to create an element with an optional class and text content. 117 function createElement(name, classes = null, textContent = null) { 118 let node = document.createElement(name); 119 if (classes) { 120 node.className = classes; 121 } 122 if (textContent) { 123 node.textContent = textContent; 124 } 125 return node; 126 } 127 128 document.addEventListener("DOMContentLoaded", function () { 129 let rawEl = document.getElementById("json-raw"); 130 if (!rawEl) { 131 return; 132 } 133 let rawText = rawEl.textContent; 134 135 let data; 136 let parseError = null; 137 try { 138 data = JSON.parse(rawText); 139 } catch (e) { 140 parseError = e; 141 } 142 143 // Build the page structure 144 document.body.innerHTML = ""; 145 146 // Toolbar 147 let toolbar = createElement("div"); 148 toolbar.id = "toolbar"; 149 let smartBtn = null; 150 let prettyBtn = createElement("button", "active", "Pretty"); 151 let rawBtn = createElement("button", null, "Raw"); 152 153 // Smart view container (may or may not be used) 154 let smartView = createElement("div"); 155 smartView.id = "smart-view"; 156 157 // Pretty view 158 let viewer = createElement("div"); 159 viewer.id = "viewer"; 160 161 // Raw view 162 let rawView = createElement("pre"); 163 rawView.id = "raw-view"; 164 165 // Check for AT Protocol smart view 166 let smartViewerPromise = null; 167 if (!parseError && location.protocol === "at:" && data && data.value) { 168 let segments = location.pathname.split("/").filter(Boolean); 169 console.log("[json-viewer] AT protocol detected, segments:", segments); 170 if (segments.length >= 1) { 171 let collection = segments[0]; 172 let tagName = collection.replaceAll(".", "-"); 173 let viewerFile = collection.replaceAll(".", "_"); 174 let viewerUrl = "beaver://atproto/viewers/" + viewerFile + ".js"; 175 console.log("[json-viewer] Trying smart viewer:", viewerUrl, "tag:", tagName); 176 177 smartViewerPromise = import(viewerUrl) 178 .then(() => { 179 console.log("[json-viewer] Smart viewer loaded successfully"); 180 // The module registers its custom element via customElements.define(). 181 let el = document.createElement(tagName); 182 el.data = data; 183 smartView.append(el); 184 return true; 185 }) 186 .catch((err) => { 187 console.log("[json-viewer] Smart viewer failed to load:", err); 188 return false; 189 }); 190 } 191 } else { 192 console.log("[json-viewer] No AT protocol detected. protocol:", location.protocol, "has value:", !!(data && data.value)); 193 } 194 195 function buildToolbar(hasSmartView) { 196 if (hasSmartView) { 197 smartBtn = createElement("button", "active", "Smart"); 198 toolbar.append(smartBtn); 199 // Demote pretty button to inactive 200 prettyBtn.className = ""; 201 } 202 toolbar.append(prettyBtn); 203 toolbar.append(rawBtn); 204 document.body.append(toolbar); 205 206 document.body.append(smartView); 207 document.body.append(viewer); 208 document.body.append(rawView); 209 210 if (hasSmartView) { 211 smartView.style.display = "block"; 212 viewer.style.display = "none"; 213 } 214 215 // Wire up toggle buttons 216 function activateView(activeBtn, showEl) { 217 if (smartBtn) smartBtn.className = ""; 218 prettyBtn.className = ""; 219 rawBtn.className = ""; 220 activeBtn.className = "active"; 221 222 smartView.style.display = "none"; 223 viewer.style.display = "none"; 224 rawView.style.display = "none"; 225 showEl.style.display = "block"; 226 } 227 228 if (smartBtn) { 229 smartBtn.onclick = () => activateView(smartBtn, smartView); 230 } 231 prettyBtn.onclick = () => activateView(prettyBtn, viewer); 232 rawBtn.onclick = () => activateView(rawBtn, rawView); 233 } 234 235 // Populate pretty and raw views 236 if (parseError) { 237 let errDiv = createElement( 238 "div", 239 "json-error", 240 "Invalid JSON: " + parseError.message, 241 ); 242 viewer.append(errDiv); 243 let pre = createElement("pre", null, rawText); 244 viewer.append(pre); 245 rawView.textContent = rawText; 246 buildToolbar(false); 247 } else { 248 renderNode(data, viewer); 249 rawView.textContent = JSON.stringify(data, null, 2); 250 251 if (smartViewerPromise) { 252 smartViewerPromise.then((ok) => buildToolbar(ok)); 253 } else { 254 buildToolbar(false); 255 } 256 } 257 258 function renderNode(value, container) { 259 if (value === null) { 260 let s = createElement("span", "json-null", "null"); 261 container.append(s); 262 } else if (typeof value === "boolean") { 263 let s = createElement("span", "json-boolean", String(value)); 264 container.append(s); 265 } else if (typeof value === "number") { 266 let s = createElement("span", "json-number", String(value)); 267 container.append(s); 268 } else if (typeof value === "string") { 269 let s = createElement("span", "json-string", JSON.stringify(value)); 270 container.append(s); 271 } else if (Array.isArray(value)) { 272 renderArray(value, container); 273 } else if (typeof value === "object") { 274 renderObject(value, container); 275 } 276 } 277 278 function renderObject(obj, container) { 279 let keys = Object.keys(obj); 280 if (keys.length === 0) { 281 container.append(createElement("span", "bracket", "{}")); 282 return; 283 } 284 285 let toggle = createElement("span", "toggle"); 286 container.append(toggle); 287 288 container.append(createElement("span", "bracket", "{")); 289 290 let inner = createElement("div", "collapsible"); 291 container.append(inner); 292 293 keys.forEach((key, i) => { 294 let line = createElement("div", "line"); 295 line.append(createElement("span", "json-key", JSON.stringify(key))); 296 line.append(document.createTextNode(": ")); 297 renderNode(obj[key], line); 298 if (i < keys.length - 1) { 299 line.append(createElement("span", "comma", ",")); 300 } 301 inner.append(line); 302 }); 303 304 container.append(createElement("span", "bracket", "}")); 305 306 toggle.onclick = function () { 307 toggle.classList.toggle("collapsed"); 308 inner.classList.toggle("hidden"); 309 }; 310 } 311 312 function renderArray(arr, container) { 313 if (arr.length === 0) { 314 container.append(createElement("span", "bracket", "[]")); 315 return; 316 } 317 318 let toggle = createElement("span", "toggle"); 319 container.append(toggle); 320 321 container.append(createElement("span", "bracket", "[")); 322 323 let inner = createElement("div", "collapsible"); 324 container.append(inner); 325 326 arr.forEach((item, i) => { 327 let line = createElement("div", "line"); 328 renderNode(item, line); 329 if (i < arr.length - 1) { 330 line.append(createElement("span", "comma", ",")); 331 } 332 inner.append(line); 333 }); 334 335 container.append(createElement("span", "bracket", "]")); 336 337 toggle.onclick = function () { 338 toggle.classList.toggle("collapsed"); 339 inner.classList.toggle("hidden"); 340 }; 341 } 342 }); 343</script> 344</head> 345 <body> 346 <pre id="json-raw">