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 1016 lines 38 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>Semantic Search - Lazurite</title> 7 <link rel="preconnect" href="https://fonts.googleapis.com" /> 8 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 9 <link href="https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap" rel="stylesheet" /> 10 <link 11 href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" 12 rel="stylesheet" /> 13 <link rel="stylesheet" href="styles.css" /> 14 <style> 15 .saved-container { 16 padding-bottom: 88px; 17 } 18 19 /* Tab bar under header */ 20 .saved-tabs { 21 display: flex; 22 border-bottom: 1px solid var(--border); 23 background-color: var(--bg); 24 } 25 26 .saved-tab { 27 flex: 1; 28 padding: 14px; 29 text-align: center; 30 font-weight: 600; 31 font-size: 15px; 32 color: var(--text-secondary); 33 cursor: pointer; 34 border-bottom: 2px solid transparent; 35 transition: all 0.2s ease; 36 background: none; 37 border-top: none; 38 border-left: none; 39 border-right: none; 40 } 41 42 .saved-tab:hover { 43 background-color: var(--surface); 44 color: var(--text-primary); 45 } 46 47 .saved-tab.active { 48 color: var(--text-primary); 49 border-bottom-color: var(--accent-primary); 50 } 51 52 /* Search input area */ 53 .semantic-search-bar { 54 padding: 12px 16px; 55 border-bottom: 1px solid var(--border); 56 } 57 58 .semantic-input-wrapper { 59 position: relative; 60 } 61 62 .semantic-input-wrapper svg { 63 position: absolute; 64 left: 12px; 65 top: 50%; 66 transform: translateY(-50%); 67 width: 18px; 68 height: 18px; 69 color: var(--text-muted); 70 } 71 72 .semantic-input { 73 width: 100%; 74 padding: 10px 12px 10px 38px; 75 border: 1px solid var(--border); 76 border-radius: 9999px; 77 background-color: var(--surface); 78 color: var(--text-primary); 79 font-size: 15px; 80 font-family: inherit; 81 transition: border-color 0.2s ease; 82 } 83 84 .semantic-input:focus { 85 outline: none; 86 border-color: var(--accent-primary); 87 } 88 89 .semantic-input::placeholder { 90 color: var(--text-muted); 91 } 92 93 /* Scope chips */ 94 .scope-chips { 95 display: flex; 96 gap: 8px; 97 padding: 10px 16px; 98 border-bottom: 1px solid var(--border); 99 } 100 101 .scope-chip { 102 padding: 6px 14px; 103 border-radius: 9999px; 104 font-size: 13px; 105 font-weight: 500; 106 border: 1.5px solid var(--border); 107 background: none; 108 color: var(--text-secondary); 109 cursor: pointer; 110 transition: all 0.2s ease; 111 font-family: inherit; 112 } 113 114 .scope-chip:hover { 115 border-color: var(--accent-primary); 116 color: var(--accent-primary); 117 } 118 119 .scope-chip.active { 120 background-color: var(--accent-primary); 121 border-color: var(--accent-primary); 122 color: white; 123 } 124 125 /* Relevance badge */ 126 .relevance-badge { 127 display: inline-flex; 128 align-items: center; 129 gap: 4px; 130 padding: 2px 8px; 131 border-radius: 9999px; 132 font-size: 11px; 133 font-weight: 600; 134 font-family: var(--font-mono); 135 white-space: nowrap; 136 } 137 138 .relevance-high { 139 background-color: rgba(34, 197, 94, 0.15); 140 color: var(--accent-success); 141 } 142 143 .relevance-medium { 144 background-color: rgba(245, 158, 11, 0.15); 145 color: var(--accent-warning); 146 } 147 148 .relevance-low { 149 background-color: rgba(168, 168, 168, 0.15); 150 color: var(--text-muted); 151 } 152 153 /* Source tag */ 154 .source-tag { 155 display: inline-flex; 156 align-items: center; 157 gap: 4px; 158 padding: 2px 8px; 159 border-radius: 4px; 160 font-size: 11px; 161 font-weight: 500; 162 background-color: var(--surface-variant); 163 color: var(--text-secondary); 164 } 165 166 .source-tag svg { 167 width: 12px; 168 height: 12px; 169 } 170 171 /* Result meta row */ 172 .result-meta { 173 display: flex; 174 align-items: center; 175 gap: 8px; 176 margin-bottom: 8px; 177 } 178 179 /* Indexing progress bar */ 180 .indexing-bar { 181 padding: 12px 16px; 182 background-color: var(--surface); 183 border-bottom: 1px solid var(--border); 184 display: flex; 185 align-items: center; 186 gap: 12px; 187 } 188 189 .indexing-bar-text { 190 font-size: 13px; 191 color: var(--text-secondary); 192 white-space: nowrap; 193 } 194 195 .indexing-progress { 196 flex: 1; 197 height: 4px; 198 background-color: var(--surface-variant); 199 border-radius: 2px; 200 overflow: hidden; 201 } 202 203 .indexing-progress-fill { 204 height: 100%; 205 background-color: var(--accent-primary); 206 border-radius: 2px; 207 transition: width 0.3s ease; 208 } 209 210 .indexing-count { 211 font-size: 12px; 212 font-family: var(--font-mono); 213 color: var(--text-muted); 214 white-space: nowrap; 215 } 216 217 /* Settings-specific styles */ 218 .settings-section { 219 margin-bottom: 24px; 220 } 221 222 .settings-section-title { 223 padding: 16px; 224 font-size: 13px; 225 font-weight: 600; 226 color: var(--text-muted); 227 text-transform: uppercase; 228 letter-spacing: 0.5px; 229 } 230 231 .settings-group { 232 background-color: var(--surface); 233 border-top: 1px solid var(--border); 234 border-bottom: 1px solid var(--border); 235 } 236 237 .slider-row { 238 display: flex; 239 align-items: center; 240 gap: 12px; 241 padding: 0 16px 16px; 242 } 243 244 .slider-track { 245 flex: 1; 246 height: 4px; 247 background-color: var(--surface-variant); 248 border-radius: 2px; 249 position: relative; 250 } 251 252 .slider-fill { 253 position: absolute; 254 height: 100%; 255 background-color: var(--accent-primary); 256 border-radius: 2px; 257 width: 25%; 258 } 259 260 .slider-thumb { 261 position: absolute; 262 top: -6px; 263 left: 25%; 264 transform: translateX(-50%); 265 width: 16px; 266 height: 16px; 267 border-radius: 50%; 268 background-color: var(--accent-primary); 269 border: 2px solid white; 270 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); 271 } 272 273 .slider-value { 274 font-size: 14px; 275 font-weight: 600; 276 font-family: var(--font-mono); 277 color: var(--text-primary); 278 min-width: 24px; 279 text-align: right; 280 } 281 282 .slider-labels { 283 display: flex; 284 justify-content: space-between; 285 padding: 0 16px 12px; 286 } 287 288 .slider-label { 289 font-size: 11px; 290 color: var(--text-muted); 291 } 292 293 /* Unavailable banner */ 294 .unavailable-banner { 295 margin: 16px; 296 padding: 16px; 297 background-color: var(--surface); 298 border: 1px solid var(--border); 299 border-radius: 12px; 300 display: flex; 301 align-items: flex-start; 302 gap: 12px; 303 } 304 305 .unavailable-banner svg { 306 width: 24px; 307 height: 24px; 308 color: var(--accent-warning); 309 flex-shrink: 0; 310 margin-top: 2px; 311 } 312 313 .unavailable-banner-text { 314 font-size: 14px; 315 color: var(--text-secondary); 316 line-height: 1.5; 317 } 318 319 .unavailable-banner-title { 320 font-weight: 600; 321 color: var(--text-primary); 322 margin-bottom: 4px; 323 } 324 325 /* View switcher */ 326 .view-switcher { 327 display: flex; 328 gap: 12px; 329 padding: 16px; 330 justify-content: center; 331 } 332 333 .view-switcher-btn { 334 padding: 8px 20px; 335 border-radius: 9999px; 336 font-size: 13px; 337 font-weight: 600; 338 border: 1.5px solid var(--border); 339 background: none; 340 color: var(--text-secondary); 341 cursor: pointer; 342 transition: all 0.2s ease; 343 font-family: inherit; 344 } 345 346 .view-switcher-btn:hover { 347 border-color: var(--accent-primary); 348 color: var(--accent-primary); 349 } 350 351 .view-switcher-btn.active-view { 352 background-color: var(--accent-primary); 353 border-color: var(--accent-primary); 354 color: white; 355 } 356 </style> 357 </head> 358 <body> 359 <div class="mobile-container"> 360 <!-- Header --> 361 <header class="header"> 362 <div style="display: flex; align-items: center; gap: 12px"> 363 <button 364 style="background: none; border: none; color: var(--text-primary); cursor: pointer; padding: 4px" 365 title="Back"> 366 <svg 367 width="24" 368 height="24" 369 viewBox="0 0 24 24" 370 fill="none" 371 stroke="currentColor" 372 stroke-width="2" 373 stroke-linecap="round" 374 stroke-linejoin="round"> 375 <polyline points="15 18 9 12 15 6" /> 376 </svg> 377 </button> 378 <h1 class="header-title">Saved Posts</h1> 379 </div> 380 </header> 381 382 <!-- Tabs: All Saved / Search --> 383 <div class="saved-tabs"> 384 <button class="saved-tab" data-view="all">All Saved</button> 385 <button class="saved-tab active" data-view="search">Search</button> 386 </div> 387 388 <!-- ==================== --> 389 <!-- VIEW: SEARCH (active) --> 390 <!-- ==================== --> 391 <div class="saved-container" id="view-search"> 392 <!-- Search input --> 393 <div class="semantic-search-bar"> 394 <div class="semantic-input-wrapper"> 395 <svg 396 viewBox="0 0 24 24" 397 fill="none" 398 stroke="currentColor" 399 stroke-width="2" 400 stroke-linecap="round" 401 stroke-linejoin="round"> 402 <circle cx="11" cy="11" r="8" /> 403 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 404 </svg> 405 <input class="semantic-input" type="text" placeholder="Search your saved posts..." value="federation decentralized protocol" /> 406 </div> 407 </div> 408 409 <!-- Scope chips --> 410 <div class="scope-chips"> 411 <button class="scope-chip active">Both</button> 412 <button class="scope-chip">Saved</button> 413 <button class="scope-chip">Liked</button> 414 </div> 415 416 <!-- Search results --> 417 <article class="post-card"> 418 <div class="result-meta"> 419 <span class="relevance-badge relevance-high">94%</span> 420 <span class="source-tag"> 421 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 422 <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" /> 423 </svg> 424 Saved 425 </span> 426 </div> 427 <div class="post-header"> 428 <div class="avatar">PF</div> 429 <div class="post-author"> 430 <div class="post-author-name">Paul Frazee</div> 431 <div class="post-author-handle">@pfrazee.com · <span class="post-timestamp">Mar 12</span></div> 432 </div> 433 </div> 434 <div class="post-content"> 435 We just shipped a major update to the <a href="#" class="post-facet">@atproto</a> federation code. Self-hosting your own PDS is now easier than ever. The decentralized web is happening. 436 </div> 437 <div class="post-actions"> 438 <button class="post-action"> 439 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 440 <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" /> 441 </svg> 442 67 443 </button> 444 <button class="post-action"> 445 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 446 <polyline points="17 1 21 5 17 9" /><path d="M3 11V9a4 4 0 0 1 4-4h14" /><polyline points="7 23 3 19 7 15" /><path d="M21 13v2a4 4 0 0 1-4 4H3" /> 447 </svg> 448 34 449 </button> 450 <button class="post-action"> 451 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 452 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 453 </svg> 454 512 455 </button> 456 </div> 457 </article> 458 459 <article class="post-card"> 460 <div class="result-meta"> 461 <span class="relevance-badge relevance-high">87%</span> 462 <span class="source-tag"> 463 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 464 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 465 </svg> 466 Liked 467 </span> 468 </div> 469 <div class="post-header"> 470 <div class="avatar">JG</div> 471 <div class="post-author"> 472 <div class="post-author-name">Jake Gold</div> 473 <div class="post-author-handle">@jake.bsky.social · <span class="post-timestamp">Mar 8</span></div> 474 </div> 475 </div> 476 <div class="post-content"> 477 The <a href="#" class="post-facet">#atproto</a> ecosystem is growing fast. More and more third-party apps are being built on the open protocol. Federation changes everything about how we think about social networks. 478 </div> 479 <div class="post-actions"> 480 <button class="post-action"> 481 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 482 <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" /> 483 </svg> 484 23 485 </button> 486 <button class="post-action"> 487 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 488 <polyline points="17 1 21 5 17 9" /><path d="M3 11V9a4 4 0 0 1 4-4h14" /><polyline points="7 23 3 19 7 15" /><path d="M21 13v2a4 4 0 0 1-4 4H3" /> 489 </svg> 490 11 491 </button> 492 <button class="post-action"> 493 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 494 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 495 </svg> 496 178 497 </button> 498 </div> 499 </article> 500 501 <article class="post-card"> 502 <div class="result-meta"> 503 <span class="relevance-badge relevance-medium">72%</span> 504 <span class="source-tag"> 505 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 506 <path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" /> 507 </svg> 508 Saved 509 </span> 510 </div> 511 <div class="post-header"> 512 <div class="avatar">SW</div> 513 <div class="post-author"> 514 <div class="post-author-name">Sarah Wu</div> 515 <div class="post-author-handle">@sarahwu.dev · <span class="post-timestamp">Feb 21</span></div> 516 </div> 517 </div> 518 <div class="post-content"> 519 Just set up my own PDS on a $5/mo VPS. The documentation has gotten so much better. If you're interested in running your own node on the AT Protocol network, now is the time. 520 </div> 521 <div class="post-actions"> 522 <button class="post-action"> 523 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 524 <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" /> 525 </svg> 526 41 527 </button> 528 <button class="post-action"> 529 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 530 <polyline points="17 1 21 5 17 9" /><path d="M3 11V9a4 4 0 0 1 4-4h14" /><polyline points="7 23 3 19 7 15" /><path d="M21 13v2a4 4 0 0 1-4 4H3" /> 531 </svg> 532 8 533 </button> 534 <button class="post-action"> 535 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 536 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 537 </svg> 538 95 539 </button> 540 </div> 541 </article> 542 543 <article class="post-card"> 544 <div class="result-meta"> 545 <span class="relevance-badge relevance-low">58%</span> 546 <span class="source-tag"> 547 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 548 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 549 </svg> 550 Liked 551 </span> 552 </div> 553 <div class="post-header"> 554 <div class="avatar">MR</div> 555 <div class="post-author"> 556 <div class="post-author-name">Mark Rivera</div> 557 <div class="post-author-handle">@mrivera.bsky.social · <span class="post-timestamp">Jan 30</span></div> 558 </div> 559 </div> 560 <div class="post-content"> 561 Interesting thread comparing ActivityPub and AT Protocol approaches to decentralization. Both have trade-offs but I think the data portability story is stronger with atproto. 562 </div> 563 <div class="post-actions"> 564 <button class="post-action"> 565 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 566 <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" /> 567 </svg> 568 156 569 </button> 570 <button class="post-action"> 571 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 572 <polyline points="17 1 21 5 17 9" /><path d="M3 11V9a4 4 0 0 1 4-4h14" /><polyline points="7 23 3 19 7 15" /><path d="M21 13v2a4 4 0 0 1-4 4H3" /> 573 </svg> 574 52 575 </button> 576 <button class="post-action"> 577 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 578 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" /> 579 </svg> 580 389 581 </button> 582 </div> 583 </article> 584 </div> 585 586 <!-- ====================== --> 587 <!-- VIEW: EMPTY STATE --> 588 <!-- ====================== --> 589 <div class="saved-container" id="view-empty" style="display: none"> 590 <div class="semantic-search-bar"> 591 <div class="semantic-input-wrapper"> 592 <svg 593 viewBox="0 0 24 24" 594 fill="none" 595 stroke="currentColor" 596 stroke-width="2" 597 stroke-linecap="round" 598 stroke-linejoin="round"> 599 <circle cx="11" cy="11" r="8" /> 600 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 601 </svg> 602 <input class="semantic-input" type="text" placeholder="Search your saved posts..." /> 603 </div> 604 </div> 605 606 <div class="scope-chips"> 607 <button class="scope-chip active">Both</button> 608 <button class="scope-chip">Saved</button> 609 <button class="scope-chip">Liked</button> 610 </div> 611 612 <div class="empty-state" style="padding-top: 80px"> 613 <svg 614 class="empty-state-icon" 615 viewBox="0 0 24 24" 616 fill="none" 617 stroke="currentColor" 618 stroke-width="1.5" 619 stroke-linecap="round" 620 stroke-linejoin="round"> 621 <circle cx="11" cy="11" r="8" /> 622 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 623 <path d="M8 11h6" /> 624 <path d="M11 8v6" /> 625 </svg> 626 <div class="empty-state-title">Semantic Search</div> 627 <div class="empty-state-text">Search your saved and liked posts by meaning, not just keywords</div> 628 </div> 629 </div> 630 631 <!-- ======================== --> 632 <!-- VIEW: NO RESULTS --> 633 <!-- ======================== --> 634 <div class="saved-container" id="view-noresults" style="display: none"> 635 <div class="semantic-search-bar"> 636 <div class="semantic-input-wrapper"> 637 <svg 638 viewBox="0 0 24 24" 639 fill="none" 640 stroke="currentColor" 641 stroke-width="2" 642 stroke-linecap="round" 643 stroke-linejoin="round"> 644 <circle cx="11" cy="11" r="8" /> 645 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 646 </svg> 647 <input class="semantic-input" type="text" placeholder="Search your saved posts..." value="quantum computing hardware" /> 648 </div> 649 </div> 650 651 <div class="scope-chips"> 652 <button class="scope-chip active">Both</button> 653 <button class="scope-chip">Saved</button> 654 <button class="scope-chip">Liked</button> 655 </div> 656 657 <div class="empty-state" style="padding-top: 80px"> 658 <svg 659 class="empty-state-icon" 660 viewBox="0 0 24 24" 661 fill="none" 662 stroke="currentColor" 663 stroke-width="1.5" 664 stroke-linecap="round" 665 stroke-linejoin="round"> 666 <circle cx="11" cy="11" r="8" /> 667 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 668 </svg> 669 <div class="empty-state-title">No similar posts found</div> 670 <div class="empty-state-text">Try a different search or broaden your scope</div> 671 </div> 672 </div> 673 674 <!-- ============================ --> 675 <!-- VIEW: INDEXING IN PROGRESS --> 676 <!-- ============================ --> 677 <div class="saved-container" id="view-indexing" style="display: none"> 678 <div class="semantic-search-bar"> 679 <div class="semantic-input-wrapper"> 680 <svg 681 viewBox="0 0 24 24" 682 fill="none" 683 stroke="currentColor" 684 stroke-width="2" 685 stroke-linecap="round" 686 stroke-linejoin="round"> 687 <circle cx="11" cy="11" r="8" /> 688 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 689 </svg> 690 <input class="semantic-input" type="text" placeholder="Search your saved posts..." disabled /> 691 </div> 692 </div> 693 694 <div class="indexing-bar"> 695 <div class="indexing-bar-text">Indexing posts...</div> 696 <div class="indexing-progress"> 697 <div class="indexing-progress-fill" style="width: 47%"></div> 698 </div> 699 <div class="indexing-count">142/300</div> 700 </div> 701 702 <div class="empty-state" style="padding-top: 60px"> 703 <svg 704 class="empty-state-icon" 705 viewBox="0 0 24 24" 706 fill="none" 707 stroke="currentColor" 708 stroke-width="1.5" 709 stroke-linecap="round" 710 stroke-linejoin="round"> 711 <path d="M12 2v4" /><path d="M12 18v4" /><path d="M4.93 4.93l2.83 2.83" /><path d="M16.24 16.24l2.83 2.83" /><path d="M2 12h4" /><path d="M18 12h4" /><path d="M4.93 19.07l2.83-2.83" /><path d="M16.24 7.76l2.83-2.83" /> 712 </svg> 713 <div class="empty-state-title">Building search index</div> 714 <div class="empty-state-text">Your saved and liked posts are being indexed for semantic search. This happens once.</div> 715 </div> 716 </div> 717 718 <!-- ========================== --> 719 <!-- VIEW: UNAVAILABLE --> 720 <!-- ========================== --> 721 <div class="saved-container" id="view-unavailable" style="display: none"> 722 <div class="unavailable-banner"> 723 <svg 724 viewBox="0 0 24 24" 725 fill="none" 726 stroke="currentColor" 727 stroke-width="2" 728 stroke-linecap="round" 729 stroke-linejoin="round"> 730 <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" /> 731 <line x1="12" y1="9" x2="12" y2="13" /> 732 <line x1="12" y1="17" x2="12.01" y2="17" /> 733 </svg> 734 <div> 735 <div class="unavailable-banner-title">Semantic search unavailable</div> 736 <div class="unavailable-banner-text"> 737 The embedding model could not be loaded on this device. Semantic search requires a device that supports on-device ML inference. 738 </div> 739 </div> 740 </div> 741 </div> 742 743 <!-- ========================== --> 744 <!-- VIEW: SETTINGS SECTION --> 745 <!-- ========================== --> 746 <div class="saved-container" id="view-settings" style="display: none"> 747 <div class="settings-section"> 748 <div class="settings-section-title">Search</div> 749 750 <div class="settings-group"> 751 <!-- Toggle --> 752 <div class="settings-item"> 753 <div class="settings-item-left"> 754 <svg 755 class="settings-item-icon" 756 viewBox="0 0 24 24" 757 fill="none" 758 stroke="currentColor" 759 stroke-width="2" 760 stroke-linecap="round" 761 stroke-linejoin="round"> 762 <circle cx="11" cy="11" r="8" /> 763 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 764 </svg> 765 <div class="settings-item-content"> 766 <div class="settings-item-title">Semantic Search</div> 767 <div class="settings-item-subtitle">Search saved posts by meaning</div> 768 </div> 769 </div> 770 <div class="settings-item-right"> 771 <div class="toggle active"> 772 <div class="toggle-thumb"></div> 773 </div> 774 </div> 775 </div> 776 777 <!-- Search scope --> 778 <div class="settings-item"> 779 <div class="settings-item-left"> 780 <svg 781 class="settings-item-icon" 782 viewBox="0 0 24 24" 783 fill="none" 784 stroke="currentColor" 785 stroke-width="2" 786 stroke-linecap="round" 787 stroke-linejoin="round"> 788 <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" /> 789 </svg> 790 <div class="settings-item-content"> 791 <div class="settings-item-title">Default Scope</div> 792 <div class="settings-item-subtitle">Both</div> 793 </div> 794 </div> 795 <div class="settings-item-right"> 796 <svg 797 width="20" 798 height="20" 799 viewBox="0 0 24 24" 800 fill="none" 801 stroke="currentColor" 802 stroke-width="2" 803 stroke-linecap="round" 804 stroke-linejoin="round"> 805 <polyline points="9 18 15 12 9 6" /> 806 </svg> 807 </div> 808 </div> 809 810 <!-- Index status --> 811 <div class="settings-item"> 812 <div class="settings-item-left"> 813 <svg 814 class="settings-item-icon" 815 viewBox="0 0 24 24" 816 fill="none" 817 stroke="currentColor" 818 stroke-width="2" 819 stroke-linecap="round" 820 stroke-linejoin="round"> 821 <ellipse cx="12" cy="5" rx="9" ry="3" /> 822 <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /> 823 <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" /> 824 </svg> 825 <div class="settings-item-content"> 826 <div class="settings-item-title">Index Status</div> 827 <div class="settings-item-subtitle">847 posts indexed</div> 828 </div> 829 </div> 830 <div class="settings-item-right"> 831 <button 832 style=" 833 padding: 6px 14px; 834 border-radius: 9999px; 835 border: 1.5px solid var(--border); 836 background: none; 837 color: var(--text-secondary); 838 font-size: 13px; 839 font-weight: 500; 840 cursor: pointer; 841 font-family: inherit; 842 "> 843 Re-index 844 </button> 845 </div> 846 </div> 847 848 <!-- Max results slider --> 849 <div class="settings-item" style="flex-direction: column; align-items: stretch; gap: 8px"> 850 <div style="display: flex; align-items: center; justify-content: space-between"> 851 <div class="settings-item-left"> 852 <svg 853 class="settings-item-icon" 854 viewBox="0 0 24 24" 855 fill="none" 856 stroke="currentColor" 857 stroke-width="2" 858 stroke-linecap="round" 859 stroke-linejoin="round"> 860 <line x1="4" y1="21" x2="4" y2="14" /><line x1="4" y1="10" x2="4" y2="3" /> 861 <line x1="12" y1="21" x2="12" y2="12" /><line x1="12" y1="8" x2="12" y2="3" /> 862 <line x1="20" y1="21" x2="20" y2="16" /><line x1="20" y1="12" x2="20" y2="3" /> 863 <line x1="1" y1="14" x2="7" y2="14" /><line x1="9" y1="8" x2="15" y2="8" /><line x1="17" y1="16" x2="23" y2="16" /> 864 </svg> 865 <div class="settings-item-content"> 866 <div class="settings-item-title">Max Results</div> 867 </div> 868 </div> 869 <div class="slider-value">20</div> 870 </div> 871 <div class="slider-row"> 872 <div class="slider-track"> 873 <div class="slider-fill"></div> 874 <div class="slider-thumb"></div> 875 </div> 876 </div> 877 <div class="slider-labels"> 878 <span class="slider-label">10</span> 879 <span class="slider-label">50</span> 880 </div> 881 </div> 882 </div> 883 </div> 884 </div> 885 886 <!-- View switcher (wireframe navigation) --> 887 <div style=" 888 position: fixed; 889 bottom: 100px; 890 left: 50%; 891 transform: translateX(-50%); 892 z-index: 200; 893 background-color: var(--surface); 894 border: 1px solid var(--border); 895 border-radius: 16px; 896 padding: 8px; 897 display: flex; 898 gap: 6px; 899 flex-wrap: wrap; 900 max-width: 390px; 901 justify-content: center; 902 box-shadow: 0 4px 12px rgba(0,0,0,0.15); 903 "> 904 <button class="view-switcher-btn active-view" data-target="view-search">Results</button> 905 <button class="view-switcher-btn" data-target="view-empty">Empty</button> 906 <button class="view-switcher-btn" data-target="view-noresults">No Results</button> 907 <button class="view-switcher-btn" data-target="view-indexing">Indexing</button> 908 <button class="view-switcher-btn" data-target="view-unavailable">Unavailable</button> 909 <button class="view-switcher-btn" data-target="view-settings">Settings</button> 910 </div> 911 912 <!-- Bottom Navigation --> 913 <nav class="nav-bar"> 914 <a href="home.html" class="nav-item"> 915 <svg 916 viewBox="0 0 24 24" 917 fill="none" 918 stroke="currentColor" 919 stroke-width="2" 920 stroke-linecap="round" 921 stroke-linejoin="round"> 922 <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /> 923 <polyline points="9 22 9 12 15 12 15 22" /> 924 </svg> 925 <span>Home</span> 926 </a> 927 928 <a href="search.html" class="nav-item"> 929 <svg 930 viewBox="0 0 24 24" 931 fill="none" 932 stroke="currentColor" 933 stroke-width="2" 934 stroke-linecap="round" 935 stroke-linejoin="round"> 936 <circle cx="11" cy="11" r="8" /> 937 <line x1="21" y1="21" x2="16.65" y2="16.65" /> 938 </svg> 939 <span>Search</span> 940 </a> 941 942 <a href="profile.html" class="nav-item"> 943 <svg 944 viewBox="0 0 24 24" 945 fill="none" 946 stroke="currentColor" 947 stroke-width="2" 948 stroke-linecap="round" 949 stroke-linejoin="round"> 950 <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /> 951 <circle cx="12" cy="7" r="4" /> 952 </svg> 953 <span>Profile</span> 954 </a> 955 956 <a href="settings.html" class="nav-item"> 957 <svg 958 viewBox="0 0 24 24" 959 fill="none" 960 stroke="currentColor" 961 stroke-width="2" 962 stroke-linecap="round" 963 stroke-linejoin="round"> 964 <circle cx="12" cy="12" r="3" /> 965 <path 966 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" /> 967 </svg> 968 <span>Settings</span> 969 </a> 970 </nav> 971 </div> 972 973 <script> 974 /* Theme init */ 975 (function () { 976 const mode = localStorage.getItem("appearance-mode") || "dark"; 977 if (mode === "dark") { 978 document.documentElement.setAttribute("data-theme", "dark"); 979 } else if (mode === "system") { 980 const prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; 981 if (prefersDark) document.documentElement.setAttribute("data-theme", "dark"); 982 } 983 })(); 984 985 /* Tab switching */ 986 document.querySelectorAll(".saved-tab").forEach((tab) => { 987 tab.addEventListener("click", () => { 988 document.querySelectorAll(".saved-tab").forEach((t) => t.classList.remove("active")); 989 tab.classList.add("active"); 990 }); 991 }); 992 993 /* Scope chip switching */ 994 document.querySelectorAll(".scope-chips").forEach((row) => { 995 row.querySelectorAll(".scope-chip").forEach((chip) => { 996 chip.addEventListener("click", () => { 997 row.querySelectorAll(".scope-chip").forEach((c) => c.classList.remove("active")); 998 chip.classList.add("active"); 999 }); 1000 }); 1001 }); 1002 1003 /* View switcher for wireframe preview */ 1004 document.querySelectorAll(".view-switcher-btn").forEach((btn) => { 1005 btn.addEventListener("click", () => { 1006 document.querySelectorAll(".view-switcher-btn").forEach((b) => b.classList.remove("active-view")); 1007 btn.classList.add("active-view"); 1008 1009 const target = btn.dataset.target; 1010 document.querySelectorAll(".saved-container").forEach((v) => (v.style.display = "none")); 1011 document.getElementById(target).style.display = "block"; 1012 }); 1013 }); 1014 </script> 1015 </body> 1016</html>