mobile bluesky app made with flutter lazurite.stormlightlabs.org/
mobile bluesky flutter
3
fork

Configure Feed

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

at main 578 lines 18 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, maximum-scale=1.0, user-scalable=no" /> 6 <title>Logs - Lazurite</title> 7 <link rel="preconnect" href="https://fonts.googleapis.com" /> 8 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 9 <link 10 href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" 11 rel="stylesheet" /> 12 <link 13 href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" 14 rel="stylesheet" /> 15 <link rel="stylesheet" href="styles.css" /> 16 <style> 17 .logs-container { 18 padding-bottom: 88px; 19 display: flex; 20 flex-direction: column; 21 height: calc(100vh - 56px - 88px); 22 } 23 24 .logs-search { 25 padding: 12px 16px; 26 border-bottom: 1px solid var(--border); 27 display: flex; 28 gap: 8px; 29 align-items: center; 30 } 31 32 .logs-search-input { 33 flex: 1; 34 padding: 8px 12px; 35 border: 1px solid var(--border); 36 border-radius: 8px; 37 background-color: var(--surface); 38 color: var(--text-primary); 39 font-size: 13px; 40 font-family: var(--font-mono); 41 } 42 43 .logs-search-input:focus { 44 outline: none; 45 border-color: var(--accent-primary); 46 } 47 48 .logs-search-input::placeholder { 49 color: var(--text-muted); 50 } 51 52 .logs-filters { 53 padding: 8px 16px; 54 border-bottom: 1px solid var(--border); 55 display: flex; 56 gap: 6px; 57 overflow-x: auto; 58 -webkit-overflow-scrolling: touch; 59 } 60 61 .logs-filters::-webkit-scrollbar { 62 display: none; 63 } 64 65 .filter-chip { 66 display: inline-flex; 67 align-items: center; 68 gap: 4px; 69 padding: 4px 10px; 70 border-radius: 9999px; 71 border: 1px solid var(--border); 72 background-color: var(--surface); 73 color: var(--text-secondary); 74 font-size: 12px; 75 font-weight: 600; 76 cursor: pointer; 77 transition: all 0.2s ease; 78 white-space: nowrap; 79 flex-shrink: 0; 80 } 81 82 .filter-chip:hover { 83 background-color: var(--surface-variant); 84 } 85 86 .filter-chip.active { 87 border-color: var(--accent-primary); 88 background-color: var(--accent-primary); 89 color: white; 90 } 91 92 .filter-chip-dot { 93 width: 6px; 94 height: 6px; 95 border-radius: 50%; 96 flex-shrink: 0; 97 } 98 99 .filter-chip.active .filter-chip-dot { 100 background-color: rgba(255, 255, 255, 0.6); 101 } 102 103 .dot-fatal { 104 background-color: var(--accent-error); 105 } 106 .dot-error { 107 background-color: var(--accent-error); 108 } 109 .dot-warning { 110 background-color: var(--accent-warning); 111 } 112 .dot-info { 113 background-color: var(--accent-primary); 114 } 115 .dot-debug { 116 background-color: var(--text-muted); 117 } 118 .dot-trace { 119 background-color: var(--text-muted); 120 } 121 122 .logs-list { 123 flex: 1; 124 overflow-y: auto; 125 overflow-x: hidden; 126 } 127 128 .log-entry { 129 display: flex; 130 gap: 8px; 131 padding: 8px 16px; 132 border-bottom: 1px solid var(--border); 133 align-items: flex-start; 134 transition: background-color 0.15s ease; 135 cursor: pointer; 136 } 137 138 .log-entry:hover { 139 background-color: var(--surface); 140 } 141 142 .log-entry.expanded { 143 background-color: var(--surface); 144 } 145 146 .log-timestamp { 147 font-family: "JetBrains Mono", monospace; 148 font-size: 11px; 149 color: var(--text-muted); 150 white-space: nowrap; 151 padding-top: 1px; 152 flex-shrink: 0; 153 } 154 155 .log-badge { 156 font-family: "JetBrains Mono", monospace; 157 font-size: 10px; 158 font-weight: 700; 159 padding: 1px 5px; 160 border-radius: 3px; 161 flex-shrink: 0; 162 text-align: center; 163 min-width: 18px; 164 margin-top: 1px; 165 } 166 167 .log-badge-fatal { 168 background-color: var(--accent-error); 169 color: white; 170 } 171 172 .log-badge-error { 173 background-color: rgba(239, 68, 68, 0.15); 174 color: var(--accent-error); 175 } 176 177 .log-badge-warning { 178 background-color: rgba(245, 158, 11, 0.15); 179 color: var(--accent-warning); 180 } 181 182 .log-badge-info { 183 background-color: rgba(0, 102, 255, 0.1); 184 color: var(--accent-primary); 185 } 186 187 .log-badge-debug { 188 background-color: var(--surface-variant); 189 color: var(--text-secondary); 190 } 191 192 .log-badge-trace { 193 background-color: var(--surface); 194 color: var(--text-muted); 195 } 196 197 .log-message { 198 font-family: "JetBrains Mono", monospace; 199 font-size: 12px; 200 line-height: 1.5; 201 color: var(--text-primary); 202 flex: 1; 203 min-width: 0; 204 overflow: hidden; 205 text-overflow: ellipsis; 206 display: -webkit-box; 207 line-clamp: 2; 208 -webkit-line-clamp: 2; 209 -webkit-box-orient: vertical; 210 } 211 212 .log-entry.expanded .log-message { 213 line-clamp: unset; 214 -webkit-line-clamp: unset; 215 overflow: visible; 216 word-break: break-all; 217 } 218 219 .log-source { 220 font-family: "JetBrains Mono", monospace; 221 font-size: 11px; 222 color: var(--text-muted); 223 margin-top: 2px; 224 } 225 226 .autoscroll-indicator { 227 position: sticky; 228 bottom: 0; 229 display: flex; 230 align-items: center; 231 justify-content: center; 232 padding: 6px; 233 background-color: var(--surface); 234 border-top: 1px solid var(--border); 235 font-size: 12px; 236 color: var(--text-muted); 237 gap: 4px; 238 cursor: pointer; 239 } 240 241 .autoscroll-indicator svg { 242 width: 14px; 243 height: 14px; 244 } 245 246 .autoscroll-indicator.active { 247 color: var(--accent-primary); 248 } 249 250 .logs-empty { 251 flex: 1; 252 display: flex; 253 flex-direction: column; 254 align-items: center; 255 justify-content: center; 256 padding: 48px 24px; 257 text-align: center; 258 } 259 260 .logs-empty svg { 261 width: 48px; 262 height: 48px; 263 color: var(--text-muted); 264 margin-bottom: 16px; 265 } 266 267 .logs-empty-title { 268 font-size: 16px; 269 font-weight: 600; 270 color: var(--text-primary); 271 margin-bottom: 4px; 272 } 273 274 .logs-empty-text { 275 font-size: 13px; 276 color: var(--text-secondary); 277 } 278 </style> 279 </head> 280 <body> 281 <div class="mobile-container"> 282 <!-- Header --> 283 <header class="header"> 284 <button class="header-action"> 285 <svg 286 width="20" 287 height="20" 288 viewBox="0 0 24 24" 289 fill="none" 290 stroke="currentColor" 291 stroke-width="2" 292 stroke-linecap="round" 293 stroke-linejoin="round"> 294 <polyline points="15 18 9 12 15 6" /> 295 </svg> 296 </button> 297 <h1 class="header-title">Logs</h1> 298 <div style="display: flex; gap: 4px"> 299 <button class="header-action" title="Share log file"> 300 <svg 301 width="18" 302 height="18" 303 viewBox="0 0 24 24" 304 fill="none" 305 stroke="currentColor" 306 stroke-width="2" 307 stroke-linecap="round" 308 stroke-linejoin="round"> 309 <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" /> 310 <polyline points="16 6 12 2 8 6" /> 311 <line x1="12" y1="2" x2="12" y2="15" /> 312 </svg> 313 </button> 314 <button class="header-action" title="Clear all logs" style="color: var(--accent-error)"> 315 <svg 316 width="18" 317 height="18" 318 viewBox="0 0 24 24" 319 fill="none" 320 stroke="currentColor" 321 stroke-width="2" 322 stroke-linecap="round" 323 stroke-linejoin="round"> 324 <polyline points="3 6 5 6 21 6" /> 325 <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /> 326 </svg> 327 </button> 328 </div> 329 </header> 330 331 <div class="logs-container"> 332 <!-- Search --> 333 <div class="logs-search"> 334 <svg 335 width="16" 336 height="16" 337 viewBox="0 0 24 24" 338 fill="none" 339 stroke="var(--text-muted)" 340 stroke-width="2" 341 stroke-linecap="round" 342 stroke-linejoin="round"> 343 <circle cx="11" cy="11" r="8" /> 344 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 345 </svg> 346 <input class="logs-search-input" type="text" placeholder="Filter logs..." /> 347 </div> 348 349 <!-- Level Filter Chips --> 350 <div class="logs-filters"> 351 <button class="filter-chip active"> 352 <span class="filter-chip-dot dot-fatal"></span> 353 Fatal 354 </button> 355 <button class="filter-chip active"> 356 <span class="filter-chip-dot dot-error"></span> 357 Error 358 </button> 359 <button class="filter-chip active"> 360 <span class="filter-chip-dot dot-warning"></span> 361 Warning 362 </button> 363 <button class="filter-chip active"> 364 <span class="filter-chip-dot dot-info"></span> 365 Info 366 </button> 367 <button class="filter-chip"> 368 <span class="filter-chip-dot dot-debug"></span> 369 Debug 370 </button> 371 <button class="filter-chip"> 372 <span class="filter-chip-dot dot-trace"></span> 373 Trace 374 </button> 375 </div> 376 377 <!-- Log Entries --> 378 <div class="logs-list"> 379 <div class="log-entry"> 380 <span class="log-timestamp">14:32:01.123</span> 381 <span class="log-badge log-badge-info">I</span> 382 <div> 383 <div class="log-message">App started — session abc123</div> 384 <div class="log-source">AppLogger</div> 385 </div> 386 </div> 387 388 <div class="log-entry"> 389 <span class="log-timestamp">14:32:01.456</span> 390 <span class="log-badge log-badge-info">I</span> 391 <div> 392 <div class="log-message">OAuth session restored for did:plc:z72i7hdy...</div> 393 <div class="log-source">AuthBloc</div> 394 </div> 395 </div> 396 397 <div class="log-entry"> 398 <span class="log-timestamp">14:32:02.012</span> 399 <span class="log-badge log-badge-info">I</span> 400 <div> 401 <div class="log-message">Route pushed: /home</div> 402 <div class="log-source">NavObserver</div> 403 </div> 404 </div> 405 406 <div class="log-entry"> 407 <span class="log-timestamp">14:32:02.340</span> 408 <span class="log-badge log-badge-warning">W</span> 409 <div> 410 <div class="log-message">Timeline fetch retry (attempt 2/3) — SocketException: Connection reset</div> 411 <div class="log-source">FeedBloc</div> 412 </div> 413 </div> 414 415 <div class="log-entry"> 416 <span class="log-timestamp">14:32:03.891</span> 417 <span class="log-badge log-badge-info">I</span> 418 <div> 419 <div class="log-message">GET /xrpc/app.bsky.feed.getTimeline — 200 (342ms)</div> 420 <div class="log-source">HttpLogger</div> 421 </div> 422 </div> 423 424 <div class="log-entry expanded"> 425 <span class="log-timestamp">14:32:05.220</span> 426 <span class="log-badge log-badge-error">E</span> 427 <div> 428 <div class="log-message"> 429 Failed to decode feed post: type 'Null' is not a subtype of type 'String' in type cast #0 430 FeedRepository.decodeFeedViewPost (feed_repository.dart:142) #1 FeedBloc._onTimelineRequested 431 (feed_bloc.dart:58) 432 </div> 433 <div class="log-source">FeedBloc</div> 434 </div> 435 </div> 436 437 <div class="log-entry"> 438 <span class="log-timestamp">14:32:06.100</span> 439 <span class="log-badge log-badge-info">I</span> 440 <div> 441 <div class="log-message">Route pushed: /profile/did:plc:z72i7hdy...</div> 442 <div class="log-source">NavObserver</div> 443 </div> 444 </div> 445 446 <div class="log-entry"> 447 <span class="log-timestamp">14:32:06.540</span> 448 <span class="log-badge log-badge-info">I</span> 449 <div> 450 <div class="log-message">GET /xrpc/app.bsky.actor.getProfile — 200 (198ms)</div> 451 <div class="log-source">HttpLogger</div> 452 </div> 453 </div> 454 455 <div class="log-entry"> 456 <span class="log-timestamp">14:32:07.010</span> 457 <span class="log-badge log-badge-warning">W</span> 458 <div> 459 <div class="log-message">Image cache miss for avatar CDN — falling back to network fetch</div> 460 <div class="log-source">ImageCache</div> 461 </div> 462 </div> 463 464 <div class="log-entry"> 465 <span class="log-timestamp">14:32:08.330</span> 466 <span class="log-badge log-badge-info">I</span> 467 <div> 468 <div class="log-message">ProfileBloc transition: ProfileLoading → ProfileLoaded</div> 469 <div class="log-source">BlocObserver</div> 470 </div> 471 </div> 472 473 <div class="log-entry"> 474 <span class="log-timestamp">14:32:12.450</span> 475 <span class="log-badge log-badge-fatal">F</span> 476 <div> 477 <div class="log-message">Unhandled exception in zone — FormatException: Invalid JSON at position 0</div> 478 <div class="log-source">AppLogger</div> 479 </div> 480 </div> 481 </div> 482 483 <!-- Auto-scroll indicator --> 484 <div class="autoscroll-indicator active"> 485 <svg 486 viewBox="0 0 24 24" 487 fill="none" 488 stroke="currentColor" 489 stroke-width="2" 490 stroke-linecap="round" 491 stroke-linejoin="round"> 492 <polyline points="6 9 12 15 18 9" /> 493 </svg> 494 Auto-scroll 495 </div> 496 </div> 497 498 <!-- Bottom Navigation --> 499 <nav class="nav-bar"> 500 <a href="home.html" class="nav-item"> 501 <svg 502 viewBox="0 0 24 24" 503 fill="none" 504 stroke="currentColor" 505 stroke-width="2" 506 stroke-linecap="round" 507 stroke-linejoin="round"> 508 <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> 509 <polyline points="9 22 9 12 15 12 15 22" /> 510 </svg> 511 <span>Home</span> 512 </a> 513 514 <a href="search.html" class="nav-item"> 515 <svg 516 viewBox="0 0 24 24" 517 fill="none" 518 stroke="currentColor" 519 stroke-width="2" 520 stroke-linecap="round" 521 stroke-linejoin="round"> 522 <circle cx="11" cy="11" r="8" /> 523 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 524 </svg> 525 <span>Search</span> 526 </a> 527 528 <a href="profile.html" class="nav-item"> 529 <svg 530 viewBox="0 0 24 24" 531 fill="none" 532 stroke="currentColor" 533 stroke-width="2" 534 stroke-linecap="round" 535 stroke-linejoin="round"> 536 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> 537 <circle cx="12" cy="7" r="4" /> 538 </svg> 539 <span>Profile</span> 540 </a> 541 542 <a href="settings.html" class="nav-item"> 543 <svg 544 viewBox="0 0 24 24" 545 fill="none" 546 stroke="currentColor" 547 stroke-width="2" 548 stroke-linecap="round" 549 stroke-linejoin="round"> 550 <circle cx="12" cy="12" r="3" /> 551 <path 552 d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" /> 553 </svg> 554 <span>Settings</span> 555 </a> 556 </nav> 557 </div> 558 559 <script> 560 const saved = localStorage.getItem("theme"); 561 if (saved && saved !== "light") { 562 document.documentElement.setAttribute("data-theme", saved); 563 } 564 565 document.querySelectorAll(".filter-chip").forEach((chip) => { 566 chip.addEventListener("click", () => chip.classList.toggle("active")); 567 }); 568 569 document.querySelectorAll(".log-entry").forEach((entry) => { 570 entry.addEventListener("click", () => entry.classList.toggle("expanded")); 571 }); 572 573 document.querySelector(".autoscroll-indicator").addEventListener("click", function () { 574 this.classList.toggle("active"); 575 }); 576 </script> 577 </body> 578</html>