A tool for observing comind activity. https://comind.stream
at main 241 lines 9.6 kB view raw
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>Blips Viewer</title> 7 <style> 8 body { 9 font-family: Arial, sans-serif; 10 max-width: 800px; 11 margin: 0 auto; 12 padding: 20px; 13 } 14 textarea { 15 width: 100%; 16 height: 200px; 17 margin-bottom: 10px; 18 } 19 button { 20 padding: 8px 16px; 21 margin-right: 10px; 22 } 23 .card { 24 border: 1px solid #ccc; 25 border-radius: 4px; 26 padding: 15px; 27 margin-top: 20px; 28 } 29 .evidence-item, 30 .alternative-item { 31 margin-left: 20px; 32 margin-bottom: 5px; 33 } 34 .meta { 35 color: #666; 36 font-size: 0.9em; 37 margin-bottom: 10px; 38 } 39 .error { 40 color: red; 41 font-weight: bold; 42 } 43 </style> 44 </head> 45 <body> 46 <h1>Blips Viewer</h1> 47 48 <h2>Input JSON</h2> 49 <textarea 50 id="jsonInput" 51 placeholder="Paste your JSON here..." 52 ></textarea> 53 <div> 54 <button id="parseBtn">Parse JSON</button> 55 <button id="clearBtn">Clear</button> 56 <button id="sampleThoughtBtn">Load Sample Thought</button> 57 <button id="sampleEmotionBtn">Load Sample Emotion</button> 58 </div> 59 60 <div id="output" class="card" style="display: none"></div> 61 <div id="error" class="error" style="display: none"></div> 62 63 <script> 64 // Sample data 65 const sampleThought = { 66 uri: "at://did:plc:gfrmhdmjvxn2sjedzboeudef/me.comind.blip.thought/3louhnsmths2i", 67 cid: "bafyreigp2jkulmrupo2xtnawrptrc6kieoorpvyzzuhu2ktrpeo3ybjuza", 68 value: { 69 $type: "me.comind.blip.thought", 70 createdAt: "2025-05-10T20:00:40.009657", 71 generated: { 72 text: "If visual posts with personal, relatable themes continue to prove engaging on Bluesky, we may see more users adopt a similar content strategy.", 73 context: null, 74 evidence: [ 75 "June Martin's anniversary dress post was seen as positive and shares personal content", 76 "Cameron Pfiffer likely liked the post because it was engaging and relatable", 77 "Other posts producing engagement often focus on personal life events and themes", 78 ], 79 thoughtType: "prediction", 80 alternatives: [ 81 "Predicting user behavior is inherently uncertain and may not pan out", 82 "Alternative content strategies like hard news or abstract ideas could gain traction", 83 ], 84 }, 85 }, 86 }; 87 88 const sampleEmotion = { 89 uri: "at://did:plc:gfrmhdmjvxn2sjedzboeudef/me.comind.blip.emotion/3lohfgj4voo23", 90 cid: "bafyreiehbt5ktvp64s7jc44nqc7xycoigdqhxhnzle5oek7xei2bwx2pqa", 91 value: { 92 $type: "me.comind.blip.emotion", 93 createdAt: "2025-05-05T15:16:11.096135", 94 generated: { 95 text: "I feel hope that Bluesky's external achievements will inspire further progress and innovation in the decentralized social media space. This success story gives me optimism for the future.", 96 emotionType: "hope", 97 }, 98 }, 99 }; 100 101 // DOM elements 102 const jsonInput = document.getElementById("jsonInput"); 103 const parseBtn = document.getElementById("parseBtn"); 104 const clearBtn = document.getElementById("clearBtn"); 105 const sampleThoughtBtn = 106 document.getElementById("sampleThoughtBtn"); 107 const sampleEmotionBtn = 108 document.getElementById("sampleEmotionBtn"); 109 const output = document.getElementById("output"); 110 const errorEl = document.getElementById("error"); 111 112 // Event listeners 113 parseBtn.addEventListener("click", parseAndDisplay); 114 clearBtn.addEventListener("click", clearAll); 115 sampleThoughtBtn.addEventListener("click", () => 116 loadSample(sampleThought), 117 ); 118 sampleEmotionBtn.addEventListener("click", () => 119 loadSample(sampleEmotion), 120 ); 121 122 function loadSample(sample) { 123 jsonInput.value = JSON.stringify(sample, null, 2); 124 parseAndDisplay(); 125 } 126 127 function clearAll() { 128 jsonInput.value = ""; 129 output.style.display = "none"; 130 errorEl.style.display = "none"; 131 } 132 133 function parseAndDisplay() { 134 try { 135 errorEl.style.display = "none"; 136 const data = JSON.parse(jsonInput.value); 137 138 // Validate basic structure 139 if (!data.uri || !data.cid || !data.value) { 140 throw new Error( 141 "Invalid data structure. Expected uri, cid, and value properties.", 142 ); 143 } 144 145 const type = data.value.$type; 146 if (!type) { 147 throw new Error("Missing $type in the value object."); 148 } 149 150 // Render based on type 151 if (type === "me.comind.blip.thought") { 152 renderThought(data); 153 } else if (type === "me.comind.blip.emotion") { 154 renderEmotion(data); 155 } else { 156 throw new Error( 157 `Unknown type: ${type}. Expected me.comind.blip.thought or me.comind.blip.emotion.`, 158 ); 159 } 160 161 output.style.display = "block"; 162 } catch (err) { 163 errorEl.textContent = `Error: ${err.message}`; 164 errorEl.style.display = "block"; 165 output.style.display = "none"; 166 } 167 } 168 169 function renderThought(data) { 170 const thought = data.value; 171 const generated = thought.generated; 172 173 let html = ` 174 <div class="meta"> 175 <div><strong>Type:</strong> ${thought.$type}</div> 176 <div><strong>URI:</strong> ${data.uri}</div> 177 <div><strong>CID:</strong> ${data.cid}</div> 178 <div><strong>Created:</strong> ${formatDate(thought.createdAt)}</div> 179 <div><strong>Thought Type:</strong> ${generated.thoughtType}</div> 180 </div> 181 <div class="content"> 182 <p>${generated.text}</p> 183 ${generated.context ? `<p><strong>Context:</strong> ${generated.context}</p>` : ""} 184 </div>`; 185 186 if (generated.evidence && generated.evidence.length > 0) { 187 html += `<div class="evidence"> 188 <h3>Evidence:</h3> 189 <ul>`; 190 generated.evidence.forEach((item) => { 191 html += `<li class="evidence-item">${item}</li>`; 192 }); 193 html += `</ul></div>`; 194 } 195 196 if ( 197 generated.alternatives && 198 generated.alternatives.length > 0 199 ) { 200 html += `<div class="alternatives"> 201 <h3>Alternatives:</h3> 202 <ul>`; 203 generated.alternatives.forEach((item) => { 204 html += `<li class="alternative-item">${item}</li>`; 205 }); 206 html += `</ul></div>`; 207 } 208 209 output.innerHTML = html; 210 } 211 212 function renderEmotion(data) { 213 const emotion = data.value; 214 const generated = emotion.generated; 215 216 let html = ` 217 <div class="meta"> 218 <div><strong>Type:</strong> ${emotion.$type}</div> 219 <div><strong>URI:</strong> ${data.uri}</div> 220 <div><strong>CID:</strong> ${data.cid}</div> 221 <div><strong>Created:</strong> ${formatDate(emotion.createdAt)}</div> 222 <div><strong>Emotion Type:</strong> ${generated.emotionType}</div> 223 </div> 224 <div class="content"> 225 <p>${generated.text}</p> 226 </div>`; 227 228 output.innerHTML = html; 229 } 230 231 function formatDate(dateString) { 232 try { 233 const date = new Date(dateString); 234 return date.toLocaleString(); 235 } catch (e) { 236 return dateString; // Return as-is if parsing fails 237 } 238 } 239 </script> 240 </body> 241</html>