BlueSky & more on desktop lazurite.stormlightlabs.org/
tauri rust typescript bluesky appview atproto solid
2
fork

Configure Feed

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

docs: add designs

* timeline -> feeds

+3123 -92
+239
docs/designs/composer.html
··· 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>Post Composer - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + --error: #ff8080; 24 + } 25 + 26 + * { box-sizing: border-box; } 27 + 28 + body { 29 + margin: 0; 30 + min-height: 100vh; 31 + font-family: "Google Sans", "Segoe UI", sans-serif; 32 + background: 33 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 34 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 35 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 36 + var(--surface-container-lowest); 37 + color: var(--on-surface); 38 + } 39 + 40 + .gradient-btn { 41 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 42 + color: #05080f; 43 + } 44 + 45 + .glass-overlay { 46 + background: rgba(0, 0, 0, 0.85); 47 + backdrop-filter: blur(20px); 48 + } 49 + 50 + .composer-panel { 51 + background: var(--surface-container-high); 52 + } 53 + 54 + .icon-btn:hover { 55 + color: var(--primary); 56 + background: rgba(125, 175, 255, 0.1); 57 + } 58 + 59 + textarea:focus { 60 + outline: none; 61 + } 62 + 63 + .char-count-warning { 64 + color: var(--error); 65 + } 66 + 67 + .attachment-preview { 68 + background: rgba(0, 0, 0, 0.5); 69 + } 70 + </style> 71 + </head> 72 + <body class="flex items-center justify-center min-h-screen p-4"> 73 + 74 + <!-- Overlay --> 75 + <div class="fixed inset-0 glass-overlay z-40"></div> 76 + 77 + <!-- Composer Modal --> 78 + <div class="relative z-50 w-full max-w-2xl"> 79 + <div class="composer-panel rounded-2xl border border-white/10 overflow-hidden shadow-2xl" 80 + style="box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(125, 175, 255, 0.1);"> 81 + 82 + <!-- Header --> 83 + <div class="flex items-center justify-between px-6 py-4 border-b border-white/5"> 84 + <div class="flex items-center gap-3"> 85 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 86 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 87 + <path d="M18 6 6 18"/> 88 + <path d="m6 6 12 12"/> 89 + </svg> 90 + </button> 91 + <span class="text-sm font-medium">New Post</span> 92 + </div> 93 + <button class="gradient-btn px-6 py-2 rounded-full text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed"> 94 + Post 95 + </button> 96 + </div> 97 + 98 + <!-- Composer Body --> 99 + <div class="p-6"> 100 + <div class="flex gap-4"> 101 + <!-- Avatar --> 102 + <div class="shrink-0"> 103 + <div class="w-11 h-11 rounded-full overflow-hidden"> 104 + <img src="https://placehold.co/44x44/7dafff/05080f?text=U" alt="Your avatar" class="w-full h-full object-cover"> 105 + </div> 106 + </div> 107 + 108 + <!-- Input Area --> 109 + <div class="flex-1 min-w-0"> 110 + <!-- Reply Context (if replying) --> 111 + <div class="flex items-center gap-2 mb-3 text-sm" style="color: var(--on-surface-variant);"> 112 + <span>Replying to</span> 113 + <span style="color: var(--primary);">@alice.bsky.social</span> 114 + </div> 115 + 116 + <!-- Text Input --> 117 + <textarea 118 + placeholder="What's happening?" 119 + class="w-full bg-transparent text-lg leading-relaxed resize-none" 120 + style="color: var(--on-surface); min-height: 120px;" 121 + rows="4" 122 + >Just shipped the new timeline view for Lazurite! Really happy with how the animations turned out using solid-motionone. The spring physics feel so natural.</textarea> 123 + 124 + <!-- Rich Text Preview --> 125 + <div class="mt-2 text-sm" style="color: var(--on-surface-variant);"> 126 + <span>Auto-detected: </span> 127 + <span style="color: var(--primary);">@lazurite</span> 128 + <span style="color: var(--primary);"> #solidjs</span> 129 + <span style="color: var(--primary);"> #tauri</span> 130 + </div> 131 + 132 + <!-- Image Attachments --> 133 + <div class="flex gap-3 mt-4"> 134 + <div class="relative group w-32 h-32 rounded-xl overflow-hidden border border-white/10 attachment-preview"> 135 + <img src="https://placehold.co/128x128/1a1a1a/7dafff?text=Screenshot+1" alt="Attachment" class="w-full h-full object-cover"> 136 + <button class="absolute top-2 right-2 p-1.5 rounded-full bg-black/60 text-white/80 opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/80"> 137 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> 138 + <path d="M18 6 6 18"/> 139 + <path d="m6 6 12 12"/> 140 + </svg> 141 + </button> 142 + <div class="absolute bottom-2 right-2 px-2 py-0.5 rounded-full bg-black/60 text-xs"> 143 + ALT 144 + </div> 145 + </div> 146 + <div class="relative group w-32 h-32 rounded-xl overflow-hidden border border-white/10 attachment-preview"> 147 + <img src="https://placehold.co/128x128/1a1a1a/0073de?text=Screenshot+2" alt="Attachment" class="w-full h-full object-cover"> 148 + <button class="absolute top-2 right-2 p-1.5 rounded-full bg-black/60 text-white/80 opacity-0 group-hover:opacity-100 transition-opacity hover:bg-black/80"> 149 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"> 150 + <path d="M18 6 6 18"/> 151 + <path d="m6 6 12 12"/> 152 + </svg> 153 + </button> 154 + </div> 155 + <button class="w-32 h-32 rounded-xl border-2 border-dashed border-white/10 flex flex-col items-center justify-center gap-2 text-white/30 hover:text-white/50 hover:border-white/20 transition-all"> 156 + <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 157 + <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/> 158 + <line x1="12" y1="8" x2="12" y2="16"/> 159 + <line x1="8" y1="12" x2="16" y2="12"/> 160 + </svg> 161 + <span class="text-xs">Add Image</span> 162 + </button> 163 + </div> 164 + </div> 165 + </div> 166 + </div> 167 + 168 + <!-- Toolbar --> 169 + <div class="flex items-center justify-between px-6 py-4 border-t border-white/5"> 170 + <div class="flex items-center gap-1"> 171 + <button class="icon-btn p-2.5 rounded-xl text-white/40 transition-all" title="Add Image"> 172 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 173 + <rect x="3" y="3" width="18" height="18" rx="2"/> 174 + <circle cx="8.5" cy="8.5" r="1.5"/> 175 + <path d="M21 15l-5-5L5 21"/> 176 + </svg> 177 + </button> 178 + <button class="icon-btn p-2.5 rounded-xl text-white/40 transition-all" title="Add GIF"> 179 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 180 + <text x="5" y="17" font-family="sans-serif" font-size="12" font-weight="bold" fill="currentColor">GIF</text> 181 + </svg> 182 + </button> 183 + <button class="icon-btn p-2.5 rounded-xl text-white/40 transition-all" title="Add Poll"> 184 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 185 + <line x1="18" y1="20" x2="18" y2="10"/> 186 + <line x1="12" y1="20" x2="12" y2="4"/> 187 + <line x1="6" y1="20" x2="6" y2="14"/> 188 + </svg> 189 + </button> 190 + <button class="icon-btn p-2.5 rounded-xl text-white/40 transition-all" title="Emoji"> 191 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 192 + <circle cx="12" cy="12" r="10"/> 193 + <path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01"/> 194 + </svg> 195 + </button> 196 + <button class="icon-btn p-2.5 rounded-xl text-white/40 transition-all" title="Schedule"> 197 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 198 + <circle cx="12" cy="12" r="10"/> 199 + <polyline points="12 6 12 12 16 14"/> 200 + </svg> 201 + </button> 202 + <div class="w-px h-6 bg-white/10 mx-2"></div> 203 + <button class="icon-btn p-2.5 rounded-xl text-white/40 transition-all" title="Reply Settings"> 204 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 205 + <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10"/> 206 + </svg> 207 + </button> 208 + </div> 209 + 210 + <div class="flex items-center gap-4"> 211 + <!-- Character Count --> 212 + <div class="flex items-center gap-2"> 213 + <div class="relative w-8 h-8"> 214 + <svg class="w-full h-full -rotate-90" viewBox="0 0 36 36"> 215 + <path 216 + d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" 217 + fill="none" 218 + stroke="rgba(255,255,255,0.1)" 219 + stroke-width="3" 220 + /> 221 + <path 222 + d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" 223 + fill="none" 224 + stroke="#7dafff" 225 + stroke-width="3" 226 + stroke-dasharray="75, 100" 227 + /> 228 + </svg> 229 + <span class="absolute inset-0 flex items-center justify-center text-xs font-medium">234</span> 230 + </div> 231 + <span class="text-xs" style="color: var(--on-surface-variant);">/ 300</span> 232 + </div> 233 + </div> 234 + </div> 235 + </div> 236 + </div> 237 + 238 + </body> 239 + </html>
+426
docs/designs/explorer.html
··· 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>AT Explorer - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .panel { 40 + background: rgba(25, 25, 25, 0.6); 41 + border: 1px solid rgba(255, 255, 255, 0.05); 42 + } 43 + 44 + .gradient-btn { 45 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 46 + color: #05080f; 47 + } 48 + 49 + .breadcrumb-item:not(:last-child) { 50 + color: var(--primary); 51 + } 52 + 53 + .breadcrumb-item:not(:last-child):hover { 54 + text-decoration: underline; 55 + } 56 + 57 + .list-item:hover { 58 + background: rgba(255, 255, 255, 0.03); 59 + } 60 + 61 + .app-rail { 62 + width: 64px; 63 + background: var(--surface-container-lowest); 64 + } 65 + 66 + .rail-icon { 67 + width: 40px; 68 + height: 40px; 69 + display: flex; 70 + align-items: center; 71 + justify-content: center; 72 + border-radius: 12px; 73 + transition: all 0.15s ease; 74 + } 75 + 76 + .rail-icon.active { 77 + color: var(--primary); 78 + background: rgba(125, 175, 255, 0.1); 79 + } 80 + 81 + .rail-icon:not(.active) { 82 + color: var(--on-surface-variant); 83 + } 84 + 85 + .json-key { 86 + color: #7dafff; 87 + } 88 + 89 + .json-string { 90 + color: #4cd964; 91 + } 92 + 93 + .json-number { 94 + color: #ff9500; 95 + } 96 + 97 + .json-boolean { 98 + color: #ff6b6b; 99 + } 100 + </style> 101 + </head> 102 + <body class="flex"> 103 + <!-- App Rail --> 104 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 105 + <div class="mb-6"> 106 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 107 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 108 + </svg> 109 + </div> 110 + 111 + <nav class="flex flex-col gap-1 flex-1"> 112 + <button class="rail-icon" title="Timeline"> 113 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 114 + <rect x="3" y="3" width="7" height="7" rx="1"/> 115 + <rect x="14" y="3" width="7" height="7" rx="1"/> 116 + <rect x="14" y="14" width="7" height="7" rx="1"/> 117 + <rect x="3" y="14" width="7" height="7" rx="1"/> 118 + </svg> 119 + </button> 120 + 121 + <button class="rail-icon" title="Search"> 122 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 123 + <circle cx="11" cy="11" r="8"/> 124 + <path d="m21 21-4.35-4.35"/> 125 + </svg> 126 + </button> 127 + 128 + <button class="rail-icon" title="Notifications"> 129 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 130 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 131 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 132 + </svg> 133 + </button> 134 + 135 + <button class="rail-icon active" title="AT Explorer"> 136 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 137 + <circle cx="12" cy="12" r="3"/> 138 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 139 + </svg> 140 + </button> 141 + </nav> 142 + 143 + <div class="flex flex-col gap-2"> 144 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 145 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 146 + </button> 147 + </div> 148 + </aside> 149 + 150 + <!-- Main Content --> 151 + <main class="flex-1 ml-16"> 152 + <!-- URL Bar --> 153 + <header class="sticky top-0 z-40 panel backdrop-blur-xl border-b border-white/5"> 154 + <div class="px-6 py-4"> 155 + <div class="flex items-center gap-3"> 156 + <!-- Navigation Buttons --> 157 + <div class="flex gap-1"> 158 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Back"> 159 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 160 + <path d="m15 18-6-6 6-6"/> 161 + </svg> 162 + </button> 163 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Forward"> 164 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 165 + <path d="m9 18 6-6-6-6"/> 166 + </svg> 167 + </button> 168 + </div> 169 + 170 + <!-- URL Input --> 171 + <div class="flex-1 relative"> 172 + <div class="flex items-center gap-2 px-4 py-2 rounded-xl" style="background: rgba(0,0,0,0.4);"> 173 + <span class="text-sm font-mono" style="color: var(--primary);">at://</span> 174 + <input type="text" 175 + value="did:plc:abc123/app.bsky.feed.post/3l2k4j5h6g" 176 + class="flex-1 bg-transparent text-sm font-mono outline-none" 177 + style="color: var(--on-surface);"> 178 + <button class="p-1.5 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 179 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 180 + <circle cx="11" cy="11" r="8"/> 181 + <path d="m21 21-4.35-4.35"/> 182 + </svg> 183 + </button> 184 + </div> 185 + </div> 186 + 187 + <!-- Actions --> 188 + <div class="flex gap-1"> 189 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Reload"> 190 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 191 + <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/> 192 + <path d="M3 3v5h5"/> 193 + <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"/> 194 + <path d="M16 21h5v-5"/> 195 + </svg> 196 + </button> 197 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Download CAR"> 198 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 199 + <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> 200 + <polyline points="7 10 12 15 17 10"/> 201 + <line x1="12" y1="15" x2="12" y2="3"/> 202 + </svg> 203 + </button> 204 + </div> 205 + </div> 206 + </div> 207 + 208 + <!-- Breadcrumb --> 209 + <div class="px-6 pb-3 flex items-center gap-2 text-sm"> 210 + <button class="breadcrumb-item flex items-center gap-1.5 px-2 py-1 rounded-lg hover:bg-white/5 transition-colors"> 211 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 212 + <rect x="2" y="2" width="20" height="8" rx="2"/> 213 + <rect x="2" y="14" width="20" height="8" rx="2"/> 214 + </svg> 215 + PDS 216 + </button> 217 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--on-surface-variant);"> 218 + <path d="m9 18 6-6-6-6"/> 219 + </svg> 220 + <button class="breadcrumb-item flex items-center gap-1.5 px-2 py-1 rounded-lg hover:bg-white/5 transition-colors"> 221 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 222 + <circle cx="12" cy="12" r="10"/> 223 + <circle cx="12" cy="10" r="3"/> 224 + <path d="M7 20.662V19a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v1.662"/> 225 + </svg> 226 + Repository 227 + </button> 228 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--on-surface-variant);"> 229 + <path d="m9 18 6-6-6-6"/> 230 + </svg> 231 + <button class="breadcrumb-item flex items-center gap-1.5 px-2 py-1 rounded-lg hover:bg-white/5 transition-colors"> 232 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 233 + <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> 234 + <polyline points="14 2 14 8 20 8"/> 235 + </svg> 236 + Collection 237 + </button> 238 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--on-surface-variant);"> 239 + <path d="m9 18 6-6-6-6"/> 240 + </svg> 241 + <span class="px-2 py-1 font-medium" style="color: var(--on-surface);">Record</span> 242 + </div> 243 + </header> 244 + 245 + <!-- Content --> 246 + <div class="flex h-[calc(100vh-140px)]"> 247 + <!-- Left: Record Info --> 248 + <div class="flex-1 overflow-y-auto p-6"> 249 + <!-- Record Header --> 250 + <div class="mb-6"> 251 + <div class="flex items-center gap-3 mb-4"> 252 + <div class="w-12 h-12 rounded-xl flex items-center justify-center" style="background: rgba(125, 175, 255, 0.15);"> 253 + <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 254 + <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> 255 + <polyline points="14 2 14 8 20 8"/> 256 + <line x1="16" y1="13" x2="8" y2="13"/> 257 + <line x1="16" y1="17" x2="8" y2="17"/> 258 + </svg> 259 + </div> 260 + <div> 261 + <h1 class="text-lg font-medium">app.bsky.feed.post</h1> 262 + <p class="text-xs font-mono" style="color: var(--on-surface-variant);">3l2k4j5h6g7f8e9d</p> 263 + </div> 264 + </div> 265 + 266 + <!-- Metadata Grid --> 267 + <div class="grid grid-cols-3 gap-4"> 268 + <div class="p-3 rounded-xl bg-white/5"> 269 + <p class="text-xs uppercase tracking-wider mb-1" style="color: var(--on-surface-variant);">CID</p> 270 + <p class="text-xs font-mono truncate">bafyreibabc123...</p> 271 + </div> 272 + <div class="p-3 rounded-xl bg-white/5"> 273 + <p class="text-xs uppercase tracking-wider mb-1" style="color: var(--on-surface-variant);">Created</p> 274 + <p class="text-xs">2024-03-15T14:30:00Z</p> 275 + </div> 276 + <div class="p-3 rounded-xl bg-white/5"> 277 + <p class="text-xs uppercase tracking-wider mb-1" style="color: var(--on-surface-variant);">Indexed</p> 278 + <p class="text-xs">2024-03-15T14:30:05Z</p> 279 + </div> 280 + </div> 281 + </div> 282 + 283 + <!-- Record Content --> 284 + <div class="rounded-2xl border border-white/10 overflow-hidden"> 285 + <div class="px-4 py-3 border-b border-white/5 flex items-center justify-between" style="background: rgba(0,0,0,0.3);"> 286 + <span class="text-sm font-medium">Record Data</span> 287 + <div class="flex gap-1"> 288 + <button class="p-1.5 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Expand All"> 289 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 290 + <path d="m18 15-6-6-6 6"/> 291 + </svg> 292 + </button> 293 + <button class="p-1.5 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Copy JSON"> 294 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 295 + <rect x="9" y="9" width="13" height="13" rx="2"/> 296 + <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/> 297 + </svg> 298 + </button> 299 + </div> 300 + </div> 301 + <div class="p-4 overflow-x-auto"> 302 + <pre class="text-sm font-mono leading-relaxed"><span class="json-key">"$type"</span>: <span class="json-string">"app.bsky.feed.post"</span>, 303 + <span class="json-key">"text"</span>: <span class="json-string">"Just shipped the new timeline view!"</span>, 304 + <span class="json-key">"createdAt"</span>: <span class="json-string">"2024-03-15T14:30:00.000Z"</span>, 305 + <span class="json-key">"facets"</span>: [ 306 + { 307 + <span class="json-key">"index"</span>: { 308 + <span class="json-key">"byteStart"</span>: <span class="json-number">0</span>, 309 + <span class="json-key">"byteEnd"</span>: <span class="json-number">4</span> 310 + }, 311 + <span class="json-key">"features"</span>: [ 312 + { 313 + <span class="json-key">"$type"</span>: <span class="json-string">"app.bsky.richtext.facet#mention"</span>, 314 + <span class="json-key">"did"</span>: <span class="json-string">"did:plc:xyz789"</span> 315 + } 316 + ] 317 + } 318 + ], 319 + <span class="json-key">"reply"</span>: { 320 + <span class="json-key">"root"</span>: { 321 + <span class="json-key">"uri"</span>: <span class="json-string">"at://did:plc:abc123/app.bsky.feed.post/3l1..."</span>, 322 + <span class="json-key">"cid"</span>: <span class="json-string">"bafyrei..."</span> 323 + }, 324 + <span class="json-key">"parent"</span>: { 325 + <span class="json-key">"uri"</span>: <span class="json-string">"at://did:plc:abc123/app.bsky.feed.post/3l1..."</span>, 326 + <span class="json-key">"cid"</span>: <span class="json-string">"bafyrei..."</span> 327 + } 328 + }</pre> 329 + </div> 330 + </div> 331 + 332 + <!-- Backlinks --> 333 + <div class="mt-6"> 334 + <h3 class="text-sm font-medium mb-3">Backlinks</h3> 335 + <div class="space-y-2"> 336 + <div class="flex items-center gap-3 p-3 rounded-xl cursor-pointer transition-colors"> 337 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 338 + <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/> 339 + <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/> 340 + </svg> 341 + <div class="flex-1 min-w-0"> 342 + <p class="text-sm truncate">app.bsky.feed.like/3l2k4j5h6g7f</p> 343 + <p class="text-xs" style="color: var(--on-surface-variant);">Referenced by did:plc:xyz789</p> 344 + </div> 345 + </div> 346 + <div class="flex items-center gap-3 p-3 rounded-xl cursor-pointer transition-colors"> 347 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 348 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 349 + </svg> 350 + <div class="flex-1 min-w-0"> 351 + <p class="text-sm truncate">app.bsky.feed.post/3l2k4j5h6g7e</p> 352 + <p class="text-xs" style="color: var(--on-surface-variant);">Reply by did:plc:def456</p> 353 + </div> 354 + </div> 355 + </div> 356 + </div> 357 + </div> 358 + 359 + <!-- Right: Preview --> 360 + <div class="w-96 border-l border-white/5 p-6"> 361 + <h3 class="text-sm font-medium mb-4">Post Preview</h3> 362 + 363 + <!-- Post Card --> 364 + <div class="rounded-2xl border border-white/10 overflow-hidden" style="background: rgba(0,0,0,0.3);"> 365 + <div class="p-4"> 366 + <div class="flex items-center gap-3 mb-3"> 367 + <div class="w-10 h-10 rounded-full overflow-hidden"> 368 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Author" class="w-full h-full object-cover"> 369 + </div> 370 + <div> 371 + <p class="text-sm font-medium">User Name</p> 372 + <p class="text-xs" style="color: var(--on-surface-variant);">@handle.bsky.social</p> 373 + </div> 374 + </div> 375 + <p class="text-sm leading-relaxed" style="color: var(--on-secondary-container);"> 376 + <span style="color: var(--primary);">@mention</span> Just shipped the new timeline view! 377 + </p> 378 + <p class="text-xs mt-3" style="color: var(--on-surface-variant);">2:30 PM · Mar 15, 2024</p> 379 + </div> 380 + <div class="px-4 py-3 border-t border-white/5 flex items-center gap-4" style="background: rgba(0,0,0,0.2);"> 381 + <span class="text-xs flex items-center gap-1.5" style="color: var(--on-surface-variant);"> 382 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 383 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 384 + </svg> 385 + 5 replies 386 + </span> 387 + <span class="text-xs flex items-center gap-1.5" style="color: var(--on-surface-variant);"> 388 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 389 + <path d="M7 10v12l4-4 4 4V10"/> 390 + <path d="M5 10a7 7 0 0 1 14 0"/> 391 + </svg> 392 + 12 likes 393 + </span> 394 + </div> 395 + </div> 396 + 397 + <!-- Moderation Labels --> 398 + <div class="mt-6"> 399 + <h3 class="text-sm font-medium mb-3">Moderation Labels</h3> 400 + <div class="space-y-2"> 401 + <div class="flex items-center gap-2 p-2 rounded-lg bg-white/5"> 402 + <span class="px-2 py-0.5 rounded text-xs font-medium bg-green-500/20 text-green-400">!no-unauthenticated</span> 403 + <span class="text-xs" style="color: var(--on-surface-variant);">Hide from logged-out users</span> 404 + </div> 405 + </div> 406 + </div> 407 + 408 + <!-- Jetstream --> 409 + <div class="mt-6"> 410 + <div class="flex items-center justify-between mb-3"> 411 + <h3 class="text-sm font-medium">Jetstream Live</h3> 412 + <span class="flex items-center gap-1.5 text-xs" style="color: #4cd964;"> 413 + <span class="w-2 h-2 rounded-full bg-green-400 animate-pulse"></span> 414 + Connected 415 + </span> 416 + </div> 417 + <div class="p-3 rounded-xl bg-white/5 text-xs" style="color: var(--on-surface-variant);"> 418 + <p>Listening for updates to this record...</p> 419 + </div> 420 + </div> 421 + </div> 422 + </div> 423 + </main> 424 + 425 + </body> 426 + </html>
+3
docs/designs/lazurite.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 512 512"> 2 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 3 + </svg>
+118
docs/designs/login.html
··· 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>Login - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + @import url('https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap'); 11 + 12 + :root { 13 + --surface-container-lowest: #000000; 14 + --surface: #0e0e0e; 15 + --surface-container: #191919; 16 + --surface-container-high: #1f1f1f; 17 + --surface-container-highest: rgba(36, 36, 36, 0.7); 18 + --surface-bright: rgba(255, 255, 255, 0.05); 19 + --primary: #7dafff; 20 + --primary-dim: #0073de; 21 + --on-primary-fixed: #05080f; 22 + --on-surface: #f4f6fb; 23 + --on-surface-variant: #ababab; 24 + --on-secondary-container: #c9d1dd; 25 + } 26 + 27 + * { box-sizing: border-box; } 28 + 29 + body { 30 + margin: 0; 31 + min-height: 100vh; 32 + font-family: "Google Sans", "Segoe UI", sans-serif; 33 + background: 34 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 35 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 36 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 37 + var(--surface-container-lowest); 38 + color: var(--on-surface); 39 + } 40 + 41 + .gradient-btn { 42 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 43 + color: #05080f; 44 + } 45 + 46 + .glass-panel { 47 + background: rgba(25, 25, 25, 0.8); 48 + backdrop-filter: blur(20px); 49 + } 50 + 51 + .input-glow:focus { 52 + box-shadow: 0 0 0 1px rgba(125, 175, 255, 0.5), 0 0 20px rgba(125, 175, 255, 0.1); 53 + } 54 + </style> 55 + </head> 56 + <body class="flex items-center justify-center min-h-screen p-6"> 57 + 58 + <div class="glass-panel rounded-3xl p-8 w-full max-w-md border border-white/5"> 59 + <!-- Logo --> 60 + <div class="flex justify-center mb-8"> 61 + <svg width="64" height="64" viewBox="0 0 512 512" style="color: #7dafff;"> 62 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 63 + </svg> 64 + </div> 65 + 66 + <!-- Header --> 67 + <div class="text-center mb-8"> 68 + <h1 class="text-3xl font-medium tracking-tight mb-2" style="letter-spacing: -0.02em;">Welcome to Lazurite</h1> 69 + <p class="text-sm" style="color: var(--on-surface-variant);">Your AT Protocol desktop client</p> 70 + </div> 71 + 72 + <!-- Login Form --> 73 + <div class="space-y-5"> 74 + <div> 75 + <label class="block text-xs uppercase tracking-wider mb-2" style="color: var(--on-surface-variant);"> 76 + Bluesky Handle 77 + </label> 78 + <input type="text" 79 + placeholder="@username.bsky.social" 80 + class="input-glow w-full px-4 py-3 rounded-xl bg-black/40 border border-white/10 text-sm outline-none transition-all" 81 + style="color: var(--on-surface);"> 82 + </div> 83 + 84 + <button class="gradient-btn w-full py-3.5 rounded-full font-medium text-sm transition-transform hover:-translate-y-0.5"> 85 + Continue with OAuth 86 + </button> 87 + 88 + <div class="text-center"> 89 + <button class="text-xs transition-colors hover:text-white" 90 + style="color: var(--on-surface-variant);"> 91 + Use App Password (Dev Mode) 92 + </button> 93 + </div> 94 + </div> 95 + 96 + <!-- Divider --> 97 + <div class="flex items-center gap-4 my-6"> 98 + <div class="flex-1 h-px bg-white/10"></div> 99 + <span class="text-xs uppercase tracking-wider" style="color: var(--on-surface-variant);">or</span> 100 + <div class="flex-1 h-px bg-white/10"></div> 101 + </div> 102 + 103 + <!-- Alternative Actions --> 104 + <div class="space-y-3"> 105 + <button class="w-full py-3 rounded-xl border border-white/10 text-sm font-medium transition-colors hover:bg-white/5" 106 + style="color: var(--on-surface);"> 107 + Browse AT Protocol 108 + </button> 109 + </div> 110 + 111 + <!-- Footer --> 112 + <p class="text-center text-xs mt-8" style="color: var(--on-surface-variant);"> 113 + Secure OAuth 2.1 authentication with DPoP 114 + </p> 115 + </div> 116 + 117 + </body> 118 + </html>
+284
docs/designs/notifications.html
··· 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>Notifications - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .panel { 40 + background: rgba(25, 25, 25, 0.6); 41 + border: 1px solid rgba(255, 255, 255, 0.05); 42 + } 43 + 44 + .notification-item:hover { 45 + background: rgba(255, 255, 255, 0.03); 46 + } 47 + 48 + .tab-btn.active { 49 + color: var(--primary); 50 + border-bottom: 2px solid var(--primary); 51 + } 52 + 53 + .tab-btn:not(.active) { 54 + color: var(--on-surface-variant); 55 + } 56 + 57 + .app-rail { 58 + width: 64px; 59 + background: var(--surface-container-lowest); 60 + } 61 + 62 + .rail-icon { 63 + width: 40px; 64 + height: 40px; 65 + display: flex; 66 + align-items: center; 67 + justify-content: center; 68 + border-radius: 12px; 69 + transition: all 0.15s ease; 70 + } 71 + 72 + .rail-icon.active { 73 + color: var(--primary); 74 + background: rgba(125, 175, 255, 0.1); 75 + } 76 + 77 + .rail-icon:not(.active) { 78 + color: var(--on-surface-variant); 79 + } 80 + 81 + .unread-indicator { 82 + background: var(--primary); 83 + } 84 + </style> 85 + </head> 86 + <body class="flex"> 87 + <!-- App Rail --> 88 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 89 + <div class="mb-6"> 90 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 91 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 92 + </svg> 93 + </div> 94 + 95 + <nav class="flex flex-col gap-1 flex-1"> 96 + <button class="rail-icon" title="Timeline"> 97 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 98 + <rect x="3" y="3" width="7" height="7" rx="1"/> 99 + <rect x="14" y="3" width="7" height="7" rx="1"/> 100 + <rect x="14" y="14" width="7" height="7" rx="1"/> 101 + <rect x="3" y="14" width="7" height="7" rx="1"/> 102 + </svg> 103 + </button> 104 + 105 + <button class="rail-icon" title="Search"> 106 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 107 + <circle cx="11" cy="11" r="8"/> 108 + <path d="m21 21-4.35-4.35"/> 109 + </svg> 110 + </button> 111 + 112 + <button class="rail-icon active relative" title="Notifications"> 113 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 114 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 115 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 116 + </svg> 117 + <span class="absolute top-1 right-1 w-2.5 h-2.5 rounded-full unread-indicator"></span> 118 + </button> 119 + 120 + <button class="rail-icon" title="AT Explorer"> 121 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 122 + <circle cx="12" cy="12" r="3"/> 123 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 124 + </svg> 125 + </button> 126 + </nav> 127 + 128 + <div class="flex flex-col gap-2"> 129 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 130 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 131 + </button> 132 + </div> 133 + </aside> 134 + 135 + <!-- Main Content --> 136 + <main class="flex-1 ml-16"> 137 + <!-- Header --> 138 + <header class="sticky top-0 z-40 panel backdrop-blur-xl border-b border-white/5"> 139 + <div class="flex items-center justify-between px-6 py-4"> 140 + <h1 class="text-xl font-medium" style="letter-spacing: -0.02em;">Notifications</h1> 141 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Mark all as read"> 142 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 143 + <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/> 144 + <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/> 145 + </svg> 146 + </button> 147 + </div> 148 + 149 + <!-- Tabs --> 150 + <div class="flex px-6 border-t border-white/5"> 151 + <button class="tab-btn active px-4 py-3 text-sm font-medium flex items-center gap-2"> 152 + All 153 + <span class="px-2 py-0.5 rounded-full text-xs bg-white/10">12</span> 154 + </button> 155 + <button class="tab-btn px-4 py-3 text-sm font-medium flex items-center gap-2"> 156 + Mentions 157 + <span class="px-2 py-0.5 rounded-full text-xs bg-white/10">3</span> 158 + </button> 159 + <button class="tab-btn px-4 py-3 text-sm font-medium">Follows</button> 160 + <button class="tab-btn px-4 py-3 text-sm font-medium">Activity</button> 161 + </div> 162 + </header> 163 + 164 + <!-- Notifications List --> 165 + <div class="divide-y divide-white/5"> 166 + <!-- Like Notification --> 167 + <div class="notification-item px-6 py-4 flex gap-3 cursor-pointer transition-colors"> 168 + <div class="shrink-0 w-8 flex justify-center"> 169 + <svg width="20" height="20" fill="#ff6b6b" viewBox="0 0 24 24"> 170 + <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> 171 + </svg> 172 + </div> 173 + <div class="flex-1 min-w-0"> 174 + <div class="flex items-center gap-2 mb-1"> 175 + <div class="flex -space-x-2"> 176 + <img src="https://placehold.co/32x32/333/fff?text=A" alt="User" class="w-8 h-8 rounded-full border-2 border-black"> 177 + <img src="https://placehold.co/32x32/444/fff?text=B" alt="User" class="w-8 h-8 rounded-full border-2 border-black"> 178 + <img src="https://placehold.co/32x32/555/fff?text=C" alt="User" class="w-8 h-8 rounded-full border-2 border-black"> 179 + </div> 180 + </div> 181 + <p class="text-sm leading-relaxed"> 182 + <span class="font-medium">Alice</span>, <span class="font-medium">Bob</span>, and <span class="font-medium">12 others</span> liked your post 183 + </p> 184 + <p class="text-sm mt-1 line-clamp-2" style="color: var(--on-surface-variant);"> 185 + Just published a new article about the AT Protocol's architecture... 186 + </p> 187 + <p class="text-xs mt-2" style="color: var(--on-surface-variant);">2h ago</p> 188 + </div> 189 + <div class="w-2 h-2 rounded-full unread-indicator shrink-0 mt-1"></div> 190 + </div> 191 + 192 + <!-- Repost Notification --> 193 + <div class="notification-item px-6 py-4 flex gap-3 cursor-pointer transition-colors"> 194 + <div class="shrink-0 w-8 flex justify-center"> 195 + <svg width="20" height="20" fill="#4cd964" viewBox="0 0 24 24"> 196 + <path d="M17 1l4 4-4 4"/> 197 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 198 + <path d="M7 23l-4-4 4-4"/> 199 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 200 + </svg> 201 + </div> 202 + <div class="flex-1 min-w-0"> 203 + <div class="flex items-center gap-2 mb-1"> 204 + <img src="https://placehold.co/32x32/666/fff?text=D" alt="User" class="w-8 h-8 rounded-full"> 205 + </div> 206 + <p class="text-sm leading-relaxed"> 207 + <span class="font-medium">David</span> reposted your post 208 + </p> 209 + <p class="text-sm mt-1 line-clamp-2" style="color: var(--on-surface-variant);"> 210 + Working on a new Rust project for handling AT Protocol lexicons... 211 + </p> 212 + <p class="text-xs mt-2" style="color: var(--on-surface-variant);">4h ago</p> 213 + </div> 214 + <div class="w-2 h-2 rounded-full unread-indicator shrink-0 mt-1"></div> 215 + </div> 216 + 217 + <!-- Mention Notification --> 218 + <div class="notification-item px-6 py-4 flex gap-3 cursor-pointer transition-colors"> 219 + <div class="shrink-0 w-8 flex justify-center"> 220 + <svg width="20" height="20" fill="var(--primary)" viewBox="0 0 24 24"> 221 + <path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z"/> 222 + </svg> 223 + </div> 224 + <div class="flex-1 min-w-0"> 225 + <div class="flex items-center gap-2 mb-1"> 226 + <img src="https://placehold.co/32x32/777/fff?text=E" alt="User" class="w-8 h-8 rounded-full"> 227 + </div> 228 + <p class="text-sm leading-relaxed"> 229 + <span class="font-medium">Emma</span> mentioned you in a post 230 + </p> 231 + <p class="text-sm mt-1" style="color: var(--on-secondary-container);"> 232 + Hey <span style="color: var(--primary);">@user</span> have you seen the new features in the latest Bluesky update? Really impressive stuff! 233 + </p> 234 + <p class="text-xs mt-2" style="color: var(--on-surface-variant);">5h ago</p> 235 + </div> 236 + <div class="w-2 h-2 rounded-full unread-indicator shrink-0 mt-1"></div> 237 + </div> 238 + 239 + <!-- Follow Notification --> 240 + <div class="notification-item px-6 py-4 flex gap-3 cursor-pointer transition-colors"> 241 + <div class="shrink-0 w-8 flex justify-center"> 242 + <svg width="20" height="20" fill="#7dafff" viewBox="0 0 24 24"> 243 + <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/> 244 + </svg> 245 + </div> 246 + <div class="flex-1 min-w-0"> 247 + <div class="flex items-center gap-3"> 248 + <img src="https://placehold.co/32x32/888/fff?text=F" alt="User" class="w-10 h-10 rounded-full"> 249 + <div class="flex-1"> 250 + <p class="text-sm"> 251 + <span class="font-medium">Frank</span> followed you 252 + </p> 253 + <p class="text-xs" style="color: var(--on-surface-variant);">@frank.dev · Software Engineer</p> 254 + </div> 255 + <button class="px-4 py-1.5 rounded-full text-xs font-medium border border-white/20 hover:bg-white/5 transition-colors"> 256 + Follow back 257 + </button> 258 + </div> 259 + <p class="text-xs mt-3" style="color: var(--on-surface-variant);">Yesterday</p> 260 + </div> 261 + </div> 262 + 263 + <!-- Read Notification --> 264 + <div class="notification-item px-6 py-4 flex gap-3 cursor-pointer transition-colors opacity-60"> 265 + <div class="shrink-0 w-8 flex justify-center"> 266 + <svg width="20" height="20" fill="var(--on-surface-variant)" viewBox="0 0 24 24"> 267 + <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> 268 + </svg> 269 + </div> 270 + <div class="flex-1 min-w-0"> 271 + <div class="flex items-center gap-2 mb-1"> 272 + <img src="https://placehold.co/32x32/999/fff?text=G" alt="User" class="w-8 h-8 rounded-full"> 273 + </div> 274 + <p class="text-sm leading-relaxed"> 275 + <span class="font-medium">Grace</span> liked your reply 276 + </p> 277 + <p class="text-xs mt-2" style="color: var(--on-surface-variant);">2 days ago</p> 278 + </div> 279 + </div> 280 + </div> 281 + </main> 282 + 283 + </body> 284 + </html>
+324
docs/designs/profile.html
··· 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>Profile - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .panel { 40 + background: rgba(25, 25, 25, 0.6); 41 + border: 1px solid rgba(255, 255, 255, 0.05); 42 + } 43 + 44 + .gradient-btn { 45 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 46 + color: #05080f; 47 + } 48 + 49 + .tab-btn.active { 50 + color: var(--primary); 51 + border-bottom: 2px solid var(--primary); 52 + } 53 + 54 + .tab-btn:not(.active) { 55 + color: var(--on-surface-variant); 56 + } 57 + 58 + .post-card:hover { 59 + background: rgba(255, 255, 255, 0.03); 60 + } 61 + 62 + .app-rail { 63 + width: 64px; 64 + background: var(--surface-container-lowest); 65 + } 66 + 67 + .rail-icon { 68 + width: 40px; 69 + height: 40px; 70 + display: flex; 71 + align-items: center; 72 + justify-content: center; 73 + border-radius: 12px; 74 + transition: all 0.15s ease; 75 + } 76 + 77 + .rail-icon.active { 78 + color: var(--primary); 79 + background: rgba(125, 175, 255, 0.1); 80 + } 81 + 82 + .rail-icon:not(.active) { 83 + color: var(--on-surface-variant); 84 + } 85 + </style> 86 + </head> 87 + <body class="flex"> 88 + <!-- App Rail --> 89 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 90 + <div class="mb-6"> 91 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 92 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 93 + </svg> 94 + </div> 95 + 96 + <nav class="flex flex-col gap-1 flex-1"> 97 + <button class="rail-icon active" title="Timeline"> 98 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 99 + <rect x="3" y="3" width="7" height="7" rx="1"/> 100 + <rect x="14" y="3" width="7" height="7" rx="1"/> 101 + <rect x="14" y="14" width="7" height="7" rx="1"/> 102 + <rect x="3" y="14" width="7" height="7" rx="1"/> 103 + </svg> 104 + </button> 105 + 106 + <button class="rail-icon" title="Search"> 107 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 108 + <circle cx="11" cy="11" r="8"/> 109 + <path d="m21 21-4.35-4.35"/> 110 + </svg> 111 + </button> 112 + 113 + <button class="rail-icon" title="Notifications"> 114 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 115 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 116 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 117 + </svg> 118 + </button> 119 + 120 + <button class="rail-icon" title="AT Explorer"> 121 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 122 + <circle cx="12" cy="12" r="3"/> 123 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 124 + </svg> 125 + </button> 126 + </nav> 127 + 128 + <div class="flex flex-col gap-2"> 129 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 130 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 131 + </button> 132 + </div> 133 + </aside> 134 + 135 + <!-- Main Content --> 136 + <main class="flex-1 ml-16"> 137 + <!-- Profile Header --> 138 + <div class="relative"> 139 + <!-- Banner --> 140 + <div class="h-48 w-full" style="background: linear-gradient(135deg, rgba(125, 175, 255, 0.3) 0%, rgba(0, 115, 222, 0.3) 100%);"> 141 + <div class="w-full h-full" style="background: url('https://placehold.co/1200x300/0a0a0a/1a1a1a?text=Banner') center/cover;"></div> 142 + </div> 143 + 144 + <!-- Profile Info --> 145 + <div class="px-6 pb-4"> 146 + <div class="flex items-end justify-between -mt-16 mb-4"> 147 + <div class="relative"> 148 + <div class="w-32 h-32 rounded-full overflow-hidden border-4 border-black" style="box-shadow: 0 0 0 4px rgba(125, 175, 255, 0.3);"> 149 + <img src="https://placehold.co/128x128/7dafff/05080f?text=AC" alt="Profile" class="w-full h-full object-cover"> 150 + </div> 151 + </div> 152 + <div class="flex gap-2 mb-2"> 153 + <button class="p-2.5 rounded-full border border-white/20 hover:bg-white/5 transition-colors"> 154 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 155 + <circle cx="12" cy="12" r="1"/> 156 + <circle cx="19" cy="12" r="1"/> 157 + <circle cx="5" cy="12" r="1"/> 158 + </svg> 159 + </button> 160 + <button class="px-6 py-2.5 rounded-full text-sm font-medium border border-white/20 hover:bg-white/5 transition-colors"> 161 + Message 162 + </button> 163 + <button class="gradient-btn px-6 py-2.5 rounded-full text-sm font-medium"> 164 + Follow 165 + </button> 166 + </div> 167 + </div> 168 + 169 + <!-- Name & Handle --> 170 + <div class="mb-4"> 171 + <h1 class="text-2xl font-medium mb-1">Alice Chen</h1> 172 + <p class="text-base" style="color: var(--on-surface-variant);">@alice.bsky.social</p> 173 + </div> 174 + 175 + <!-- Bio --> 176 + <p class="text-sm leading-relaxed mb-4" style="color: var(--on-secondary-container);"> 177 + Building the future of decentralized social media. AT Protocol enthusiast. Rust & TypeScript developer. 178 + <span style="color: var(--primary);">#bluesky</span> <span style="color: var(--primary);">#atprotocol</span> 179 + </p> 180 + 181 + <!-- Metadata --> 182 + <div class="flex flex-wrap items-center gap-4 text-sm mb-4" style="color: var(--on-surface-variant);"> 183 + <span class="flex items-center gap-1.5"> 184 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 185 + <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/> 186 + <circle cx="12" cy="10" r="3"/> 187 + </svg> 188 + San Francisco, CA 189 + </span> 190 + <span class="flex items-center gap-1.5"> 191 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 192 + <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/> 193 + <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/> 194 + </svg> 195 + alicechen.dev 196 + </span> 197 + <span class="flex items-center gap-1.5"> 198 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 199 + <rect x="3" y="4" width="18" height="18" rx="2"/> 200 + <line x1="16" y1="2" x2="16" y2="6"/> 201 + <line x1="8" y1="2" x2="8" y2="6"/> 202 + <line x1="3" y1="10" x2="21" y2="10"/> 203 + </svg> 204 + Joined March 2023 205 + </span> 206 + </div> 207 + 208 + <!-- Stats --> 209 + <div class="flex gap-6 text-sm"> 210 + <button class="hover:underline"> 211 + <span class="font-bold" style="color: var(--on-surface);">1,247</span> 212 + <span style="color: var(--on-surface-variant);"> Following</span> 213 + </button> 214 + <button class="hover:underline"> 215 + <span class="font-bold" style="color: var(--on-surface);">8.5K</span> 216 + <span style="color: var(--on-surface-variant);"> Followers</span> 217 + </button> 218 + <button class="hover:underline"> 219 + <span class="font-bold" style="color: var(--on-surface);">12.3K</span> 220 + <span style="color: var(--on-surface-variant);"> Posts</span> 221 + </button> 222 + </div> 223 + </div> 224 + </div> 225 + 226 + <!-- Tabs --> 227 + <div class="sticky top-0 z-40 panel backdrop-blur-xl border-t border-b border-white/5"> 228 + <div class="flex px-6"> 229 + <button class="tab-btn active px-4 py-3 text-sm font-medium">Posts</button> 230 + <button class="tab-btn px-4 py-3 text-sm font-medium">Replies</button> 231 + <button class="tab-btn px-4 py-3 text-sm font-medium">Media</button> 232 + <button class="tab-btn px-4 py-3 text-sm font-medium flex items-center gap-2"> 233 + Publications 234 + <span class="px-2 py-0.5 rounded-full text-xs bg-white/10">3</span> 235 + </button> 236 + <button class="tab-btn px-4 py-3 text-sm font-medium">Likes</button> 237 + </div> 238 + </div> 239 + 240 + <!-- Feed --> 241 + <div class="divide-y divide-white/5"> 242 + <!-- Post 1 --> 243 + <article class="post-card px-6 py-5 transition-colors"> 244 + <div class="flex gap-3"> 245 + <div class="w-11 h-11 rounded-full overflow-hidden shrink-0"> 246 + <img src="https://placehold.co/44x44/7dafff/05080f?text=AC" alt="Author" class="w-full h-full object-cover"> 247 + </div> 248 + <div class="flex-1 min-w-0"> 249 + <div class="flex items-center gap-2 mb-1"> 250 + <span class="font-medium text-sm">Alice Chen</span> 251 + <span class="text-xs" style="color: var(--on-surface-variant);">@alice.bsky.social</span> 252 + <span class="text-xs" style="color: var(--on-surface-variant);">· 2h</span> 253 + </div> 254 + <p class="text-sm leading-relaxed mb-3" style="color: var(--on-secondary-container);"> 255 + Just published a new article about the AT Protocol's architecture and how it enables truly decentralized social media. The implications for data ownership are massive! 🧵 256 + </p> 257 + <div class="flex items-center gap-6"> 258 + <button class="flex items-center gap-1.5 text-xs p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 259 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 260 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 261 + </svg> 262 + <span>12</span> 263 + </button> 264 + <button class="flex items-center gap-1.5 text-xs p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 265 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 266 + <path d="M7 10v12l4-4 4 4V10"/> 267 + <path d="M5 10a7 7 0 0 1 14 0"/> 268 + </svg> 269 + <span>48</span> 270 + </button> 271 + <button class="flex items-center gap-1.5 text-xs p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 272 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 273 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 274 + <polyline points="16 6 12 2 8 6"/> 275 + <line x1="12" y1="2" x2="12" y2="15"/> 276 + </svg> 277 + <span>Share</span> 278 + </button> 279 + </div> 280 + </div> 281 + </div> 282 + </article> 283 + 284 + <!-- Post 2 (With Image) --> 285 + <article class="post-card px-6 py-5 transition-colors"> 286 + <div class="flex gap-3"> 287 + <div class="w-11 h-11 rounded-full overflow-hidden shrink-0"> 288 + <img src="https://placehold.co/44x44/7dafff/05080f?text=AC" alt="Author" class="w-full h-full object-cover"> 289 + </div> 290 + <div class="flex-1 min-w-0"> 291 + <div class="flex items-center gap-2 mb-1"> 292 + <span class="font-medium text-sm">Alice Chen</span> 293 + <span class="text-xs" style="color: var(--on-surface-variant);">@alice.bsky.social</span> 294 + <span class="text-xs" style="color: var(--on-surface-variant);">· 5h</span> 295 + </div> 296 + <p class="text-sm leading-relaxed mb-3" style="color: var(--on-secondary-container);"> 297 + Speaking at RustConf next month about building high-performance AT Protocol clients. Who's going? 298 + </p> 299 + <div class="rounded-2xl overflow-hidden mb-3 border border-white/5" style="max-height: 300px;"> 300 + <img src="https://placehold.co/600x300/1a1a1a/7dafff?text=RustConf+2024" alt="Conference" class="w-full h-full object-cover"> 301 + </div> 302 + <div class="flex items-center gap-6"> 303 + <button class="flex items-center gap-1.5 text-xs p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 304 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 305 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 306 + </svg> 307 + <span>23</span> 308 + </button> 309 + <button class="flex items-center gap-1.5 text-xs p-1.5 rounded-lg transition-all" style="color: var(--primary);"> 310 + <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"> 311 + <path d="M7 10v12l4-4 4 4V10"/> 312 + <path d="M5 10a7 7 0 0 1 14 0"/> 313 + </svg> 314 + <span>156</span> 315 + </button> 316 + </div> 317 + </div> 318 + </div> 319 + </article> 320 + </div> 321 + </main> 322 + 323 + </body> 324 + </html>
+348
docs/designs/publications.html
··· 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>Publications - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .panel { 40 + background: rgba(25, 25, 25, 0.6); 41 + border: 1px solid rgba(255, 255, 255, 0.05); 42 + } 43 + 44 + .gradient-btn { 45 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 46 + color: #05080f; 47 + } 48 + 49 + .publication-card:hover { 50 + background: rgba(255, 255, 255, 0.03); 51 + transform: translateY(-2px); 52 + } 53 + 54 + .document-item:hover { 55 + background: rgba(255, 255, 255, 0.03); 56 + } 57 + 58 + .app-rail { 59 + width: 64px; 60 + background: var(--surface-container-lowest); 61 + } 62 + 63 + .rail-icon { 64 + width: 40px; 65 + height: 40px; 66 + display: flex; 67 + align-items: center; 68 + justify-content: center; 69 + border-radius: 12px; 70 + transition: all 0.15s ease; 71 + } 72 + 73 + .rail-icon.active { 74 + color: var(--primary); 75 + background: rgba(125, 175, 255, 0.1); 76 + } 77 + 78 + .rail-icon:not(.active) { 79 + color: var(--on-surface-variant); 80 + } 81 + </style> 82 + </head> 83 + <body class="flex"> 84 + <!-- App Rail --> 85 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 86 + <div class="mb-6"> 87 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 88 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 89 + </svg> 90 + </div> 91 + 92 + <nav class="flex flex-col gap-1 flex-1"> 93 + <button class="rail-icon" title="Timeline"> 94 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 95 + <rect x="3" y="3" width="7" height="7" rx="1"/> 96 + <rect x="14" y="3" width="7" height="7" rx="1"/> 97 + <rect x="14" y="14" width="7" height="7" rx="1"/> 98 + <rect x="3" y="14" width="7" height="7" rx="1"/> 99 + </svg> 100 + </button> 101 + 102 + <button class="rail-icon" title="Search"> 103 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 104 + <circle cx="11" cy="11" r="8"/> 105 + <path d="m21 21-4.35-4.35"/> 106 + </svg> 107 + </button> 108 + 109 + <button class="rail-icon" title="Notifications"> 110 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 111 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 112 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 113 + </svg> 114 + </button> 115 + 116 + <button class="rail-icon" title="AT Explorer"> 117 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 118 + <circle cx="12" cy="12" r="3"/> 119 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 120 + </svg> 121 + </button> 122 + </nav> 123 + 124 + <div class="flex flex-col gap-2"> 125 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 126 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 127 + </button> 128 + </div> 129 + </aside> 130 + 131 + <!-- Main Content --> 132 + <main class="flex-1 ml-16"> 133 + <!-- Header --> 134 + <header class="sticky top-0 z-40 panel backdrop-blur-xl border-b border-white/5"> 135 + <div class="flex items-center justify-between px-6 py-4"> 136 + <div class="flex items-center gap-3"> 137 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 138 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 139 + <path d="m15 18-6-6 6-6"/> 140 + </svg> 141 + </button> 142 + <h1 class="text-xl font-medium" style="letter-spacing: -0.02em;">Publications</h1> 143 + </div> 144 + <div class="flex items-center gap-2"> 145 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Filter"> 146 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 147 + <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/> 148 + </svg> 149 + </button> 150 + <button class="gradient-btn px-4 py-2 rounded-full text-sm font-medium flex items-center gap-2"> 151 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 152 + <line x1="12" y1="5" x2="12" y2="19"/> 153 + <line x1="5" y1="12" x2="19" y2="12"/> 154 + </svg> 155 + New Publication 156 + </button> 157 + </div> 158 + </div> 159 + </header> 160 + 161 + <!-- Publications Grid --> 162 + <div class="p-6"> 163 + <div class="grid grid-cols-2 gap-4"> 164 + <!-- Publication 1 --> 165 + <div class="publication-card rounded-2xl border border-white/10 overflow-hidden cursor-pointer transition-all" style="background: rgba(0,0,0,0.3);"> 166 + <div class="h-32 w-full" style="background: linear-gradient(135deg, rgba(125, 175, 255, 0.3) 0%, rgba(0, 115, 222, 0.3) 100%);"> 167 + <div class="w-full h-full flex items-center justify-center"> 168 + <div class="w-16 h-16 rounded-2xl flex items-center justify-center" style="background: rgba(0,0,0,0.4);"> 169 + <span class="text-2xl">📝</span> 170 + </div> 171 + </div> 172 + </div> 173 + <div class="p-4"> 174 + <h3 class="text-lg font-medium mb-1">Engineering Blog</h3> 175 + <p class="text-sm mb-3 line-clamp-2" style="color: var(--on-surface-variant);">Deep dives into AT Protocol implementation details and architecture decisions.</p> 176 + <div class="flex items-center justify-between"> 177 + <span class="text-xs" style="color: var(--on-surface-variant);">24 articles</span> 178 + <button class="flex items-center gap-1 text-xs font-medium" style="color: var(--primary);"> 179 + <svg width="14" height="14" fill="currentColor" viewBox="0 0 24 24"> 180 + <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> 181 + </svg> 182 + Subscribed 183 + </button> 184 + </div> 185 + </div> 186 + </div> 187 + 188 + <!-- Publication 2 --> 189 + <div class="publication-card rounded-2xl border border-white/10 overflow-hidden cursor-pointer transition-all" style="background: rgba(0,0,0,0.3);"> 190 + <div class="h-32 w-full" style="background: linear-gradient(135deg, rgba(76, 217, 100, 0.3) 0%, rgba(0, 150, 50, 0.3) 100%);"> 191 + <div class="w-full h-full flex items-center justify-center"> 192 + <div class="w-16 h-16 rounded-2xl flex items-center justify-center" style="background: rgba(0,0,0,0.4);"> 193 + <span class="text-2xl">🌱</span> 194 + </div> 195 + </div> 196 + </div> 197 + <div class="p-4"> 198 + <h3 class="text-lg font-medium mb-1">AT Protocol Weekly</h3> 199 + <p class="text-sm mb-3 line-clamp-2" style="color: var(--on-surface-variant);">Weekly updates on the AT Protocol ecosystem, new apps, and community highlights.</p> 200 + <div class="flex items-center justify-between"> 201 + <span class="text-xs" style="color: var(--on-surface-variant);">52 issues</span> 202 + <button class="flex items-center gap-1 text-xs font-medium" style="color: var(--on-surface-variant);"> 203 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 204 + <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> 205 + </svg> 206 + Subscribe 207 + </button> 208 + </div> 209 + </div> 210 + </div> 211 + 212 + <!-- Publication 3 --> 213 + <div class="publication-card rounded-2xl border border-white/10 overflow-hidden cursor-pointer transition-all" style="background: rgba(0,0,0,0.3);"> 214 + <div class="h-32 w-full" style="background: linear-gradient(135deg, rgba(255, 107, 107, 0.3) 0%, rgba(200, 50, 50, 0.3) 100%);"> 215 + <div class="w-full h-full flex items-center justify-center"> 216 + <div class="w-16 h-16 rounded-2xl flex items-center justify-center" style="background: rgba(0,0,0,0.4);"> 217 + <span class="text-2xl">🎨</span> 218 + </div> 219 + </div> 220 + </div> 221 + <div class="p-4"> 222 + <h3 class="text-lg font-medium mb-1">Design Patterns</h3> 223 + <p class="text-sm mb-3 line-clamp-2" style="color: var(--on-surface-variant);">UI/UX patterns and design systems for decentralized applications.</p> 224 + <div class="flex items-center justify-between"> 225 + <span class="text-xs" style="color: var(--on-surface-variant);">8 articles</span> 226 + <button class="flex items-center gap-1 text-xs font-medium" style="color: var(--primary);"> 227 + <svg width="14" height="14" fill="currentColor" viewBox="0 0 24 24"> 228 + <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/> 229 + </svg> 230 + Subscribed 231 + </button> 232 + </div> 233 + </div> 234 + </div> 235 + </div> 236 + 237 + <!-- Selected Publication Documents --> 238 + <div class="mt-8"> 239 + <div class="flex items-center justify-between mb-4"> 240 + <h2 class="text-lg font-medium">Engineering Blog</h2> 241 + <div class="flex items-center gap-2"> 242 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 243 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 244 + <circle cx="12" cy="12" r="1"/> 245 + <circle cx="19" cy="12" r="1"/> 246 + <circle cx="5" cy="12" r="1"/> 247 + </svg> 248 + </button> 249 + </div> 250 + </div> 251 + 252 + <div class="space-y-2"> 253 + <!-- Document 1 --> 254 + <div class="document-item flex items-center gap-4 p-4 rounded-xl cursor-pointer transition-colors" style="background: rgba(0,0,0,0.2);"> 255 + <div class="w-12 h-16 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(125, 175, 255, 0.15);"> 256 + <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 257 + <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> 258 + <polyline points="14 2 14 8 20 8"/> 259 + <line x1="16" y1="13" x2="8" y2="13"/> 260 + <line x1="16" y1="17" x2="8" y2="17"/> 261 + </svg> 262 + </div> 263 + <div class="flex-1 min-w-0"> 264 + <h3 class="text-sm font-medium mb-1">Building a Rust AT Protocol Client</h3> 265 + <p class="text-xs line-clamp-1" style="color: var(--on-surface-variant);">A comprehensive guide to implementing AT Protocol in Rust, covering XRPC, authentication, and record operations...</p> 266 + <div class="flex items-center gap-3 mt-2 text-xs" style="color: var(--on-surface-variant);"> 267 + <span>Mar 15, 2024</span> 268 + <span>·</span> 269 + <span>12 min read</span> 270 + <span>·</span> 271 + <span>1.2K views</span> 272 + </div> 273 + </div> 274 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 275 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 276 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 277 + <polyline points="16 6 12 2 8 6"/> 278 + <line x1="12" y1="2" x2="12" y2="15"/> 279 + </svg> 280 + </button> 281 + </div> 282 + 283 + <!-- Document 2 --> 284 + <div class="document-item flex items-center gap-4 p-4 rounded-xl cursor-pointer transition-colors" style="background: rgba(0,0,0,0.2);"> 285 + <div class="w-12 h-16 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(125, 175, 255, 0.15);"> 286 + <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 287 + <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> 288 + <polyline points="14 2 14 8 20 8"/> 289 + <line x1="16" y1="13" x2="8" y2="13"/> 290 + <line x1="16" y1="17" x2="8" y2="17"/> 291 + </svg> 292 + </div> 293 + <div class="flex-1 min-w-0"> 294 + <h3 class="text-sm font-medium mb-1">Understanding Repo Structure</h3> 295 + <p class="text-xs line-clamp-1" style="color: var(--on-surface-variant);">Deep dive into the Merkle tree structure of AT Protocol repositories and how data is organized...</p> 296 + <div class="flex items-center gap-3 mt-2 text-xs" style="color: var(--on-surface-variant);"> 297 + <span>Mar 10, 2024</span> 298 + <span>·</span> 299 + <span>8 min read</span> 300 + <span>·</span> 301 + <span>890 views</span> 302 + </div> 303 + </div> 304 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 305 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 306 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 307 + <polyline points="16 6 12 2 8 6"/> 308 + <line x1="12" y1="2" x2="12" y2="15"/> 309 + </svg> 310 + </button> 311 + </div> 312 + 313 + <!-- Document 3 --> 314 + <div class="document-item flex items-center gap-4 p-4 rounded-xl cursor-pointer transition-colors" style="background: rgba(0,0,0,0.2);"> 315 + <div class="w-12 h-16 rounded-lg flex items-center justify-center shrink-0" style="background: rgba(125, 175, 255, 0.15);"> 316 + <svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 317 + <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> 318 + <polyline points="14 2 14 8 20 8"/> 319 + <line x1="16" y1="13" x2="8" y2="13"/> 320 + <line x1="16" y1="17" x2="8" y2="17"/> 321 + </svg> 322 + </div> 323 + <div class="flex-1 min-w-0"> 324 + <h3 class="text-sm font-medium mb-1">OAuth 2.1 with DPoP Explained</h3> 325 + <p class="text-xs line-clamp-1" style="color: var(--on-surface-variant);">How AT Protocol implements modern OAuth for secure, passwordless authentication...</p> 326 + <div class="flex items-center gap-3 mt-2 text-xs" style="color: var(--on-surface-variant);"> 327 + <span>Mar 5, 2024</span> 328 + <span>·</span> 329 + <span>15 min read</span> 330 + <span>·</span> 331 + <span>2.1K views</span> 332 + </div> 333 + </div> 334 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 335 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 336 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 337 + <polyline points="16 6 12 2 8 6"/> 338 + <line x1="12" y1="2" x2="12" y2="15"/> 339 + </svg> 340 + </button> 341 + </div> 342 + </div> 343 + </div> 344 + </div> 345 + </main> 346 + 347 + </body> 348 + </html>
+454
docs/designs/reading.html
··· 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>Reading View - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&family=Merriweather:wght@300;400;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .panel { 40 + background: rgba(25, 25, 25, 0.6); 41 + border: 1px solid rgba(255, 255, 255, 0.05); 42 + } 43 + 44 + .gradient-btn { 45 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 46 + color: #05080f; 47 + } 48 + 49 + .app-rail { 50 + width: 64px; 51 + background: var(--surface-container-lowest); 52 + } 53 + 54 + .rail-icon { 55 + width: 40px; 56 + height: 40px; 57 + display: flex; 58 + align-items: center; 59 + justify-content: center; 60 + border-radius: 12px; 61 + transition: all 0.15s ease; 62 + } 63 + 64 + .rail-icon.active { 65 + color: var(--primary); 66 + background: rgba(125, 175, 255, 0.1); 67 + } 68 + 69 + .rail-icon:not(.active) { 70 + color: var(--on-surface-variant); 71 + } 72 + 73 + .article-content { 74 + font-family: "Merriweather", Georgia, serif; 75 + font-size: 1.125rem; 76 + line-height: 1.8; 77 + color: var(--on-secondary-container); 78 + } 79 + 80 + .article-content h1, 81 + .article-content h2, 82 + .article-content h3 { 83 + font-family: "Google Sans", sans-serif; 84 + color: var(--on-surface); 85 + margin-top: 2em; 86 + margin-bottom: 0.75em; 87 + } 88 + 89 + .article-content p { 90 + margin-bottom: 1.5em; 91 + } 92 + 93 + .article-content code { 94 + font-family: "SF Mono", Monaco, monospace; 95 + font-size: 0.875em; 96 + padding: 0.2em 0.4em; 97 + border-radius: 4px; 98 + background: rgba(125, 175, 255, 0.1); 99 + color: var(--primary); 100 + } 101 + 102 + .article-content pre { 103 + background: rgba(0, 0, 0, 0.5); 104 + padding: 1.5em; 105 + border-radius: 12px; 106 + overflow-x: auto; 107 + margin: 1.5em 0; 108 + } 109 + 110 + .article-content pre code { 111 + background: none; 112 + padding: 0; 113 + color: var(--on-surface); 114 + } 115 + 116 + .article-content blockquote { 117 + border-left: 3px solid var(--primary); 118 + padding-left: 1.5em; 119 + margin: 1.5em 0; 120 + font-style: italic; 121 + color: var(--on-surface-variant); 122 + } 123 + 124 + .article-content a { 125 + color: var(--primary); 126 + text-decoration: none; 127 + } 128 + 129 + .article-content a:hover { 130 + text-decoration: underline; 131 + } 132 + </style> 133 + </head> 134 + <body class="flex"> 135 + <!-- App Rail --> 136 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 137 + <div class="mb-6"> 138 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 139 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 140 + </svg> 141 + </div> 142 + 143 + <nav class="flex flex-col gap-1 flex-1"> 144 + <button class="rail-icon" title="Timeline"> 145 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 146 + <rect x="3" y="3" width="7" height="7" rx="1"/> 147 + <rect x="14" y="3" width="7" height="7" rx="1"/> 148 + <rect x="14" y="14" width="7" height="7" rx="1"/> 149 + <rect x="3" y="14" width="7" height="7" rx="1"/> 150 + </svg> 151 + </button> 152 + 153 + <button class="rail-icon" title="Search"> 154 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 155 + <circle cx="11" cy="11" r="8"/> 156 + <path d="m21 21-4.35-4.35"/> 157 + </svg> 158 + </button> 159 + 160 + <button class="rail-icon" title="Notifications"> 161 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 162 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 163 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 164 + </svg> 165 + </button> 166 + 167 + <button class="rail-icon" title="AT Explorer"> 168 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 169 + <circle cx="12" cy="12" r="3"/> 170 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 171 + </svg> 172 + </button> 173 + </nav> 174 + 175 + <div class="flex flex-col gap-2"> 176 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 177 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 178 + </button> 179 + </div> 180 + </aside> 181 + 182 + <!-- Main Content --> 183 + <main class="flex-1 ml-16"> 184 + <!-- Reading Toolbar --> 185 + <header class="sticky top-0 z-40 panel backdrop-blur-xl border-b border-white/5"> 186 + <div class="flex items-center justify-between px-6 py-3"> 187 + <div class="flex items-center gap-3"> 188 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all"> 189 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 190 + <path d="m15 18-6-6 6-6"/> 191 + </svg> 192 + </button> 193 + <div class="w-px h-6 bg-white/10"></div> 194 + <span class="text-sm" style="color: var(--on-surface-variant);">Engineering Blog</span> 195 + </div> 196 + 197 + <div class="flex items-center gap-2"> 198 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Text Size"> 199 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 200 + <polyline points="4 7 4 4 20 4 20 7"/> 201 + <line x1="9" y1="20" x2="15" y2="20"/> 202 + <line x1="12" y1="4" x2="12" y2="20"/> 203 + </svg> 204 + </button> 205 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Bookmark"> 206 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 207 + <path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z"/> 208 + </svg> 209 + </button> 210 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="Share"> 211 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 212 + <circle cx="18" cy="5" r="3"/> 213 + <circle cx="6" cy="12" r="3"/> 214 + <circle cx="18" cy="19" r="3"/> 215 + <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/> 216 + <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/> 217 + </svg> 218 + </button> 219 + <button class="p-2 rounded-lg text-white/40 hover:text-white hover:bg-white/5 transition-all" title="More"> 220 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 221 + <circle cx="12" cy="12" r="1"/> 222 + <circle cx="19" cy="12" r="1"/> 223 + <circle cx="5" cy="12" r="1"/> 224 + </svg> 225 + </button> 226 + </div> 227 + </div> 228 + 229 + <!-- Reading Progress --> 230 + <div class="h-0.5 bg-white/5"> 231 + <div class="h-full gradient-btn" style="width: 35%;"></div> 232 + </div> 233 + </header> 234 + 235 + <!-- Article --> 236 + <article class="max-w-3xl mx-auto px-8 py-12"> 237 + <!-- Header --> 238 + <header class="mb-12"> 239 + <h1 class="text-4xl font-medium mb-4" style="letter-spacing: -0.02em; line-height: 1.2;"> 240 + Building a Rust AT Protocol Client 241 + </h1> 242 + <p class="text-xl mb-6" style="color: var(--on-surface-variant);"> 243 + A comprehensive guide to implementing AT Protocol in Rust, covering XRPC, authentication, and record operations. 244 + </p> 245 + 246 + <!-- Author & Meta --> 247 + <div class="flex items-center gap-4 py-6 border-y border-white/10"> 248 + <div class="w-12 h-12 rounded-full overflow-hidden"> 249 + <img src="https://placehold.co/48x48/7dafff/05080f?text=AC" alt="Author" class="w-full h-full object-cover"> 250 + </div> 251 + <div class="flex-1"> 252 + <p class="font-medium">Alice Chen</p> 253 + <p class="text-sm" style="color: var(--on-surface-variant);">@alice.bsky.social · Mar 15, 2024 · 12 min read</p> 254 + </div> 255 + <button class="gradient-btn px-6 py-2 rounded-full text-sm font-medium"> 256 + Follow 257 + </button> 258 + </div> 259 + </header> 260 + 261 + <!-- Content --> 262 + <div class="article-content"> 263 + <p> 264 + The AT Protocol represents a paradigm shift in how we think about social media infrastructure. 265 + Unlike traditional centralized platforms, it separates identity, data storage, and indexing into 266 + distinct layers, enabling unprecedented flexibility and user control. 267 + </p> 268 + 269 + <h2>Getting Started with XRPC</h2> 270 + 271 + <p> 272 + At the heart of the AT Protocol is XRPC (XML-RPC evolved), a JSON-based RPC protocol that 273 + powers all communication between clients and servers. In Rust, we can implement this using 274 + standard HTTP clients with a few ergonomic wrappers. 275 + </p> 276 + 277 + <pre><code>use atproto::xrpc::Client; 278 + 279 + let client = Client::new("https://bsky.social"); 280 + let session = client 281 + .create_session("handle.bsky.social", "password") 282 + .await?; 283 + 284 + println!("Authenticated as: {}", session.did);</code></pre> 285 + 286 + <p> 287 + The <code>Client</code> struct handles all the boilerplate of making XRPC requests, including 288 + proper lexicon validation and type-safe deserialization. It's built on top of 289 + <code>reqwest</code> and leverages Rust's async/await for efficient I/O. 290 + </p> 291 + 292 + <h2>Understanding Lexicons</h2> 293 + 294 + <p> 295 + Lexicons are the schema definitions that govern the AT Protocol. They define the shapes of 296 + records, the parameters for queries, and the structure of responses. Having strong types for 297 + these is essential for a reliable client implementation. 298 + </p> 299 + 300 + <blockquote> 301 + "Lexicons are to AT Protocol what GraphQL schemas are to GraphQL — they provide the contract 302 + that all participants in the network must follow." 303 + </blockquote> 304 + 305 + <p> 306 + In our Rust implementation, we generate types from the official lexicon definitions using a 307 + custom code generator. This ensures we're always in sync with the protocol spec and catches 308 + breaking changes at compile time. 309 + </p> 310 + 311 + <h2>Authentication with OAuth 2.1</h2> 312 + 313 + <p> 314 + The AT Protocol uses modern OAuth 2.1 with DPoP (Demonstrating Proof-of-Possession) for 315 + authentication. This provides several advantages over traditional API keys: 316 + </p> 317 + 318 + <ul style="margin-bottom: 1.5em; padding-left: 1.5em;"> 319 + <li style="margin-bottom: 0.5em;">No password storage required</li> 320 + <li style="margin-bottom: 0.5em;">Fine-grained permission scopes</li> 321 + <li style="margin-bottom: 0.5em;">Automatic token rotation</li> 322 + <li>Secure binding to client instances via DPoP</li> 323 + </ul> 324 + 325 + <p> 326 + Implementing this in Rust requires careful handling of cryptographic operations. We use the 327 + <code>jsonwebtoken</code> crate for JWT handling and <code>ring</code> for the DPoP proof 328 + generation. 329 + </p> 330 + 331 + <h2>Working with Repositories</h2> 332 + 333 + <p> 334 + User data in AT Protocol is stored in repositories — Merkle tree structures that provide 335 + cryptographic verification of data integrity. When you post, like, or follow, you're creating 336 + new records in your repository. 337 + </p> 338 + 339 + <pre><code>// Creating a new post record 340 + let record = PostRecord { 341 + text: "Hello, AT Protocol!".to_string(), 342 + created_at: Utc::now(), 343 + facets: vec![], 344 + reply: None, 345 + }; 346 + 347 + let response = client 348 + .create_record(&session, "app.bsky.feed.post", record) 349 + .await?;</code></pre> 350 + 351 + <p> 352 + The <code>create_record</code> method handles all the complexity of encoding the record in 353 + CBOR, computing its CID, and updating the repository's Merkle tree. From the developer's 354 + perspective, it feels as simple as a standard API call. 355 + </p> 356 + 357 + <h2>Conclusion</h2> 358 + 359 + <p> 360 + Building a robust AT Protocol client in Rust requires understanding the protocol's unique 361 + architecture, but the results are worth it: type-safe, performant, and fully interoperable 362 + with the growing ecosystem of ATProto applications. 363 + </p> 364 + 365 + <p> 366 + In future posts, we'll dive deeper into specific areas like implementing custom lexicons, 367 + building feed generators, and leveraging the protocol's real-time capabilities through 368 + Jetstream. 369 + </p> 370 + </div> 371 + 372 + <!-- Tags --> 373 + <div class="flex flex-wrap gap-2 mt-12 pt-8 border-t border-white/10"> 374 + <span class="px-3 py-1.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">rust</span> 375 + <span class="px-3 py-1.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">atprotocol</span> 376 + <span class="px-3 py-1.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">tutorial</span> 377 + <span class="px-3 py-1.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">decentralized</span> 378 + </div> 379 + 380 + <!-- Engagement --> 381 + <div class="flex items-center justify-between mt-8 pt-8 border-t border-white/10"> 382 + <div class="flex items-center gap-6"> 383 + <button class="flex items-center gap-2 text-sm" style="color: var(--on-surface-variant);"> 384 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 385 + <path d="M7 10v12l4-4 4 4V10"/> 386 + <path d="M5 10a7 7 0 0 1 14 0"/> 387 + </svg> 388 + 248 likes 389 + </button> 390 + <button class="flex items-center gap-2 text-sm" style="color: var(--on-surface-variant);"> 391 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 392 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 393 + </svg> 394 + 18 comments 395 + </button> 396 + <button class="flex items-center gap-2 text-sm" style="color: var(--on-surface-variant);"> 397 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 398 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 399 + <polyline points="16 6 12 2 8 6"/> 400 + <line x1="12" y1="2" x2="12" y2="15"/> 401 + </svg> 402 + Share 403 + </button> 404 + </div> 405 + <span class="text-xs" style="color: var(--on-surface-variant);">1.2K views</span> 406 + </div> 407 + </article> 408 + </main> 409 + 410 + <!-- Right Sidebar - TOC --> 411 + <aside class="fixed right-0 top-0 w-72 h-full panel border-l border-white/5 overflow-y-auto hidden xl:block"> 412 + <div class="p-6"> 413 + <h3 class="text-sm font-medium mb-4">Table of Contents</h3> 414 + <nav class="space-y-1 text-sm"> 415 + <a href="#" class="block py-1.5 px-2 rounded-lg hover:bg-white/5 transition-colors" style="color: var(--primary);"> 416 + Getting Started with XRPC 417 + </a> 418 + <a href="#" class="block py-1.5 px-2 rounded-lg hover:bg-white/5 transition-colors" style="color: var(--on-surface-variant);"> 419 + Understanding Lexicons 420 + </a> 421 + <a href="#" class="block py-1.5 px-2 rounded-lg hover:bg-white/5 transition-colors" style="color: var(--on-surface-variant);"> 422 + Authentication with OAuth 2.1 423 + </a> 424 + <a href="#" class="block py-1.5 px-2 rounded-lg hover:bg-white/5 transition-colors" style="color: var(--on-surface-variant);"> 425 + Working with Repositories 426 + </a> 427 + <a href="#" class="block py-1.5 px-2 rounded-lg hover:bg-white/5 transition-colors" style="color: var(--on-surface-variant);"> 428 + Conclusion 429 + </a> 430 + </nav> 431 + 432 + <!-- Related Articles --> 433 + <div class="mt-8 pt-6 border-t border-white/10"> 434 + <h3 class="text-sm font-medium mb-4">More from Alice</h3> 435 + <div class="space-y-3"> 436 + <a href="#" class="block group"> 437 + <p class="text-sm font-medium group-hover:text-white transition-colors" style="color: var(--on-surface-variant);"> 438 + Understanding Repo Structure 439 + </p> 440 + <p class="text-xs mt-1" style="color: var(--on-surface-variant);">8 min read</p> 441 + </a> 442 + <a href="#" class="block group"> 443 + <p class="text-sm font-medium group-hover:text-white transition-colors" style="color: var(--on-surface-variant);"> 444 + OAuth 2.1 with DPoP Explained 445 + </p> 446 + <p class="text-xs mt-1" style="color: var(--on-surface-variant);">15 min read</p> 447 + </a> 448 + </div> 449 + </div> 450 + </div> 451 + </aside> 452 + 453 + </body> 454 + </html>
+365
docs/designs/search.html
··· 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>Search - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .panel { 40 + background: rgba(25, 25, 25, 0.6); 41 + border: 1px solid rgba(255, 255, 255, 0.05); 42 + } 43 + 44 + .gradient-btn { 45 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 46 + color: #05080f; 47 + } 48 + 49 + .search-mode-btn.active { 50 + color: var(--primary); 51 + background: rgba(125, 175, 255, 0.1); 52 + } 53 + 54 + .result-card:hover { 55 + background: rgba(255, 255, 255, 0.03); 56 + } 57 + 58 + .highlight { 59 + background: rgba(125, 175, 255, 0.2); 60 + color: var(--primary); 61 + padding: 0 2px; 62 + border-radius: 2px; 63 + } 64 + 65 + .app-rail { 66 + width: 64px; 67 + background: var(--surface-container-lowest); 68 + } 69 + 70 + .rail-icon { 71 + width: 40px; 72 + height: 40px; 73 + display: flex; 74 + align-items: center; 75 + justify-content: center; 76 + border-radius: 12px; 77 + transition: all 0.15s ease; 78 + } 79 + 80 + .rail-icon.active { 81 + color: var(--primary); 82 + background: rgba(125, 175, 255, 0.1); 83 + } 84 + 85 + .rail-icon:not(.active) { 86 + color: var(--on-surface-variant); 87 + } 88 + 89 + .sync-progress { 90 + background: linear-gradient(90deg, var(--primary) 0%, var(--primary-dim) 100%); 91 + } 92 + </style> 93 + </head> 94 + <body class="flex"> 95 + <!-- App Rail --> 96 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 97 + <div class="mb-6"> 98 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 99 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 100 + </svg> 101 + </div> 102 + 103 + <nav class="flex flex-col gap-1 flex-1"> 104 + <button class="rail-icon" title="Timeline"> 105 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 106 + <rect x="3" y="3" width="7" height="7" rx="1"/> 107 + <rect x="14" y="3" width="7" height="7" rx="1"/> 108 + <rect x="14" y="14" width="7" height="7" rx="1"/> 109 + <rect x="3" y="14" width="7" height="7" rx="1"/> 110 + </svg> 111 + </button> 112 + 113 + <button class="rail-icon active" title="Search"> 114 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 115 + <circle cx="11" cy="11" r="8"/> 116 + <path d="m21 21-4.35-4.35"/> 117 + </svg> 118 + </button> 119 + 120 + <button class="rail-icon" title="Notifications"> 121 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 122 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 123 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 124 + </svg> 125 + </button> 126 + 127 + <button class="rail-icon" title="AT Explorer"> 128 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 129 + <circle cx="12" cy="12" r="3"/> 130 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 131 + </svg> 132 + </button> 133 + </nav> 134 + 135 + <div class="flex flex-col gap-2"> 136 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 137 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 138 + </button> 139 + </div> 140 + </aside> 141 + 142 + <!-- Main Content --> 143 + <main class="flex-1 ml-16 mr-80"> 144 + <!-- Header with Search --> 145 + <header class="sticky top-0 z-40 panel backdrop-blur-xl border-b border-white/5"> 146 + <div class="px-6 py-4"> 147 + <div class="relative"> 148 + <svg class="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--on-surface-variant);"> 149 + <circle cx="11" cy="11" r="8"/> 150 + <path d="m21 21-4.35-4.35"/> 151 + </svg> 152 + <input type="text" 153 + value="at protocol architecture" 154 + placeholder="Search your saved posts..." 155 + class="w-full pl-12 pr-4 py-3 rounded-2xl text-base outline-none transition-all" 156 + style="background: rgba(0,0,0,0.4); color: var(--on-surface);"> 157 + <div class="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-2"> 158 + <kbd class="px-2 py-1 rounded text-xs bg-white/10" style="color: var(--on-surface-variant);">ESC</kbd> 159 + <span class="text-xs" style="color: var(--on-surface-variant);">clear</span> 160 + </div> 161 + </div> 162 + </div> 163 + 164 + <!-- Search Modes --> 165 + <div class="flex items-center justify-between px-6 pb-4"> 166 + <div class="flex gap-2"> 167 + <button class="search-mode-btn active px-4 py-2 rounded-full text-sm font-medium transition-all flex items-center gap-2"> 168 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 169 + <circle cx="11" cy="11" r="8"/> 170 + <path d="m21 21-4.35-4.35"/> 171 + </svg> 172 + Keyword 173 + </button> 174 + <button class="search-mode-btn px-4 py-2 rounded-full text-sm font-medium transition-all flex items-center gap-2" style="color: var(--on-surface-variant);"> 175 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 176 + <circle cx="12" cy="12" r="3"/> 177 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 178 + </svg> 179 + Semantic 180 + </button> 181 + <button class="search-mode-btn px-4 py-2 rounded-full text-sm font-medium transition-all flex items-center gap-2" style="color: var(--on-surface-variant);"> 182 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 183 + <path d="M12 2L2 7l10 5 10-5-10-5z"/> 184 + <path d="M2 17l10 5 10-5"/> 185 + <path d="M2 12l10 5 10-5"/> 186 + </svg> 187 + Hybrid 188 + </button> 189 + </div> 190 + <span class="text-xs" style="color: var(--on-surface-variant);"> 191 + <kbd class="px-1.5 py-0.5 rounded bg-white/10">Tab</kbd> to switch modes 192 + </span> 193 + </div> 194 + </header> 195 + 196 + <!-- Results Info --> 197 + <div class="px-6 py-3 border-b border-white/5 flex items-center justify-between"> 198 + <span class="text-sm" style="color: var(--on-surface-variant);"> 199 + Found <span class="font-medium" style="color: var(--on-surface);">24</span> results 200 + </span> 201 + <div class="flex items-center gap-2 text-xs" style="color: var(--on-surface-variant);"> 202 + <span>Indexed:</span> 203 + <span class="font-medium" style="color: var(--primary);">1,247 posts</span> 204 + <span>· Last sync: 2h ago</span> 205 + </div> 206 + </div> 207 + 208 + <!-- Search Results --> 209 + <div class="divide-y divide-white/5"> 210 + <!-- Result 1 --> 211 + <article class="result-card px-6 py-5 cursor-pointer transition-colors"> 212 + <div class="flex gap-3"> 213 + <div class="w-10 h-10 rounded-full overflow-hidden shrink-0"> 214 + <img src="https://placehold.co/40x40/333/fff?text=A" alt="Author" class="w-full h-full object-cover"> 215 + </div> 216 + <div class="flex-1 min-w-0"> 217 + <div class="flex items-center gap-2 mb-1"> 218 + <span class="font-medium text-sm">Alice Chen</span> 219 + <span class="text-xs" style="color: var(--on-surface-variant);">@alice.bsky.social</span> 220 + <span class="text-xs" style="color: var(--on-surface-variant);">· Mar 15</span> 221 + </div> 222 + <p class="text-sm leading-relaxed mb-2" style="color: var(--on-secondary-container);"> 223 + Just published a new article about the <span class="highlight">AT Protocol</span>'s <span class="highlight">architecture</span> and how it enables truly decentralized social media. The implications for data ownership are massive! 224 + </p> 225 + <div class="flex items-center gap-4"> 226 + <span class="text-xs flex items-center gap-1.5" style="color: var(--on-surface-variant);"> 227 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 228 + <path d="M7 10v12l4-4 4 4V10"/> 229 + <path d="M5 10a7 7 0 0 1 14 0"/> 230 + </svg> 231 + 48 likes 232 + </span> 233 + <span class="text-xs flex items-center gap-1.5" style="color: var(--on-surface-variant);"> 234 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 235 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 236 + </svg> 237 + 12 replies 238 + </span> 239 + <span class="px-2 py-0.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">Liked</span> 240 + </div> 241 + </div> 242 + </div> 243 + </article> 244 + 245 + <!-- Result 2 (Semantic match) --> 246 + <article class="result-card px-6 py-5 cursor-pointer transition-colors"> 247 + <div class="flex gap-3"> 248 + <div class="w-10 h-10 rounded-full overflow-hidden shrink-0"> 249 + <img src="https://placehold.co/40x40/444/fff?text=B" alt="Author" class="w-full h-full object-cover"> 250 + </div> 251 + <div class="flex-1 min-w-0"> 252 + <div class="flex items-center gap-2 mb-1"> 253 + <span class="font-medium text-sm">Bob Martinez</span> 254 + <span class="text-xs" style="color: var(--on-surface-variant);">@bob.dev</span> 255 + <span class="text-xs" style="color: var(--on-surface-variant);">· Mar 12</span> 256 + <span class="px-2 py-0.5 rounded-full text-xs" style="background: rgba(125, 175, 255, 0.15); color: var(--primary);">Semantic match</span> 257 + </div> 258 + <p class="text-sm leading-relaxed mb-2" style="color: var(--on-secondary-container);"> 259 + The federated design of Bluesky's underlying system means users can move between providers without losing their identity or social graph. This is a game-changer for the fediverse. 260 + </p> 261 + <div class="flex items-center gap-4"> 262 + <span class="text-xs flex items-center gap-1.5" style="color: var(--on-surface-variant);"> 263 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 264 + <path d="M7 10v12l4-4 4 4V10"/> 265 + <path d="M5 10a7 7 0 0 1 14 0"/> 266 + </svg> 267 + 89 likes 268 + </span> 269 + <span class="px-2 py-0.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">Own post</span> 270 + </div> 271 + </div> 272 + </div> 273 + </article> 274 + 275 + <!-- Result 3 --> 276 + <article class="result-card px-6 py-5 cursor-pointer transition-colors"> 277 + <div class="flex gap-3"> 278 + <div class="w-10 h-10 rounded-full overflow-hidden shrink-0"> 279 + <img src="https://placehold.co/40x40/555/fff?text=C" alt="Author" class="w-full h-full object-cover"> 280 + </div> 281 + <div class="flex-1 min-w-0"> 282 + <div class="flex items-center gap-2 mb-1"> 283 + <span class="font-medium text-sm">Carol Smith</span> 284 + <span class="text-xs" style="color: var(--on-surface-variant);">@carol.tech</span> 285 + <span class="text-xs" style="color: var(--on-surface-variant);">· Mar 10</span> 286 + </div> 287 + <p class="text-sm leading-relaxed mb-2" style="color: var(--on-secondary-container);"> 288 + Working through the <span class="highlight">AT Protocol</span> specs today. The separation of identity (DID), data (repo), and indexing (AppView) is elegantly designed. <span class="highlight">Architecture</span> matters! 289 + </p> 290 + <div class="flex items-center gap-4"> 291 + <span class="text-xs flex items-center gap-1.5" style="color: var(--on-surface-variant);"> 292 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 293 + <path d="M7 10v12l4-4 4 4V10"/> 294 + <path d="M5 10a7 7 0 0 1 14 0"/> 295 + </svg> 296 + 34 likes 297 + </span> 298 + <span class="px-2 py-0.5 rounded-full text-xs bg-white/10" style="color: var(--on-surface-variant);">Bookmarked</span> 299 + </div> 300 + </div> 301 + </div> 302 + </article> 303 + </div> 304 + </main> 305 + 306 + <!-- Right Sidebar --> 307 + <aside class="fixed right-0 top-0 w-80 h-full panel border-l border-white/5 overflow-y-auto"> 308 + <!-- Sync Status --> 309 + <div class="p-4 border-b border-white/5"> 310 + <div class="flex items-center justify-between mb-3"> 311 + <h3 class="text-sm font-medium">Sync Status</h3> 312 + <span class="text-xs px-2 py-0.5 rounded-full" style="background: rgba(76, 217, 100, 0.15); color: #4cd964;">Active</span> 313 + </div> 314 + <div class="space-y-2"> 315 + <div class="flex items-center justify-between text-xs" style="color: var(--on-surface-variant);"> 316 + <span>Liked posts</span> 317 + <span>842 synced</span> 318 + </div> 319 + <div class="w-full h-1.5 rounded-full bg-white/10 overflow-hidden"> 320 + <div class="sync-progress h-full rounded-full" style="width: 100%;"></div> 321 + </div> 322 + <div class="flex items-center justify-between text-xs" style="color: var(--on-surface-variant);"> 323 + <span>Own posts</span> 324 + <span>405 synced</span> 325 + </div> 326 + <div class="w-full h-1.5 rounded-full bg-white/10 overflow-hidden"> 327 + <div class="sync-progress h-full rounded-full" style="width: 100%;"></div> 328 + </div> 329 + </div> 330 + <button class="w-full mt-4 py-2 rounded-xl text-xs font-medium border border-white/10 hover:bg-white/5 transition-colors" style="color: var(--on-surface-variant);"> 331 + Sync now 332 + </button> 333 + </div> 334 + 335 + <!-- Embedding Model --> 336 + <div class="p-4 border-b border-white/5"> 337 + <h3 class="text-sm font-medium mb-3">Embedding Model</h3> 338 + <div class="flex items-center gap-3 p-3 rounded-xl bg-white/5"> 339 + <div class="w-10 h-10 rounded-lg flex items-center justify-center" style="background: rgba(125, 175, 255, 0.15);"> 340 + <svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--primary);"> 341 + <circle cx="12" cy="12" r="3"/> 342 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 343 + </svg> 344 + </div> 345 + <div class="flex-1 min-w-0"> 346 + <p class="text-sm font-medium truncate">nomic-embed-text-v1.5</p> 347 + <p class="text-xs" style="color: var(--on-surface-variant);">768 dimensions · Cached</p> 348 + </div> 349 + </div> 350 + </div> 351 + 352 + <!-- Search Tips --> 353 + <div class="p-4"> 354 + <h3 class="text-sm font-medium mb-3">Search Tips</h3> 355 + <div class="space-y-2 text-xs" style="color: var(--on-surface-variant);"> 356 + <p><kbd class="px-1.5 py-0.5 rounded bg-white/10">/</kbd> Focus search from anywhere</p> 357 + <p><kbd class="px-1.5 py-0.5 rounded bg-white/10">Tab</kbd> Cycle search modes</p> 358 + <p>Use quotes for exact matches</p> 359 + <p>Semantic mode finds conceptually similar posts</p> 360 + </div> 361 + </div> 362 + </aside> 363 + 364 + </body> 365 + </html>
+412
docs/designs/timeline.html
··· 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>Timeline - Lazurite</title> 7 + <script src="https://cdn.tailwindcss.com"></script> 8 + <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&display=swap" rel="stylesheet"> 9 + <style> 10 + :root { 11 + --surface-container-lowest: #000000; 12 + --surface: #0e0e0e; 13 + --surface-container: #191919; 14 + --surface-container-high: #1f1f1f; 15 + --surface-container-highest: rgba(36, 36, 36, 0.7); 16 + --surface-bright: rgba(255, 255, 255, 0.05); 17 + --primary: #7dafff; 18 + --primary-dim: #0073de; 19 + --on-primary-fixed: #05080f; 20 + --on-surface: #f4f6fb; 21 + --on-surface-variant: #ababab; 22 + --on-secondary-container: #c9d1dd; 23 + } 24 + 25 + * { box-sizing: border-box; } 26 + 27 + body { 28 + margin: 0; 29 + min-height: 100vh; 30 + font-family: "Google Sans", "Segoe UI", sans-serif; 31 + background: 32 + radial-gradient(circle at 14% 12%, rgba(125, 175, 255, 0.22), transparent 32%), 33 + radial-gradient(circle at 88% 22%, rgba(0, 115, 222, 0.18), transparent 28%), 34 + radial-gradient(circle at 72% 88%, rgba(125, 175, 255, 0.12), transparent 30%), 35 + var(--surface-container-lowest); 36 + color: var(--on-surface); 37 + } 38 + 39 + .gradient-btn { 40 + background: linear-gradient(135deg, #7dafff 0%, #0073de 100%); 41 + color: #05080f; 42 + } 43 + 44 + .panel { 45 + background: rgba(25, 25, 25, 0.6); 46 + border: 1px solid rgba(255, 255, 255, 0.05); 47 + } 48 + 49 + .post-card:hover { 50 + background: rgba(255, 255, 255, 0.03); 51 + } 52 + 53 + .icon-btn:hover { 54 + color: var(--primary); 55 + background: rgba(125, 175, 255, 0.1); 56 + } 57 + 58 + .app-rail { 59 + width: 64px; 60 + background: var(--surface-container-lowest); 61 + } 62 + 63 + .rail-icon { 64 + width: 40px; 65 + height: 40px; 66 + display: flex; 67 + align-items: center; 68 + justify-content: center; 69 + border-radius: 12px; 70 + transition: all 0.15s ease; 71 + } 72 + 73 + .rail-icon.active { 74 + color: var(--primary); 75 + background: rgba(125, 175, 255, 0.1); 76 + } 77 + 78 + .rail-icon:not(.active) { 79 + color: var(--on-surface-variant); 80 + } 81 + 82 + .rail-icon:not(.active):hover { 83 + color: var(--on-surface); 84 + background: rgba(255, 255, 255, 0.05); 85 + } 86 + 87 + .avatar-ring { 88 + box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--primary); 89 + } 90 + 91 + .feed-tab.active { 92 + color: var(--primary); 93 + border-bottom: 2px solid var(--primary); 94 + } 95 + 96 + .feed-tab:not(.active) { 97 + color: var(--on-surface-variant); 98 + } 99 + </style> 100 + </head> 101 + <body class="flex"> 102 + <!-- App Rail --> 103 + <aside class="app-rail fixed left-0 top-0 h-full flex flex-col items-center py-4 z-50"> 104 + <!-- Logo --> 105 + <div class="mb-6"> 106 + <svg width="40" height="40" viewBox="0 0 512 512" style="color: #7dafff;"> 107 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 108 + </svg> 109 + </div> 110 + 111 + <!-- Navigation Icons --> 112 + <nav class="flex flex-col gap-1 flex-1"> 113 + <button class="rail-icon active" title="Timeline"> 114 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 115 + <rect x="3" y="3" width="7" height="7" rx="1"/> 116 + <rect x="14" y="3" width="7" height="7" rx="1"/> 117 + <rect x="14" y="14" width="7" height="7" rx="1"/> 118 + <rect x="3" y="14" width="7" height="7" rx="1"/> 119 + </svg> 120 + </button> 121 + 122 + <button class="rail-icon" title="Search"> 123 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 124 + <circle cx="11" cy="11" r="8"/> 125 + <path d="m21 21-4.35-4.35"/> 126 + </svg> 127 + </button> 128 + 129 + <button class="rail-icon" title="Notifications"> 130 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 131 + <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/> 132 + <path d="M13.73 21a2 2 0 0 1-3.46 0"/> 133 + </svg> 134 + <span class="absolute top-2 right-2 w-2 h-2 rounded-full bg-red-400"></span> 135 + </button> 136 + 137 + <button class="rail-icon" title="AT Explorer"> 138 + <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 139 + <circle cx="12" cy="12" r="3"/> 140 + <path d="M12 1v6m0 6v6m4.22-10.22l4.24-4.24M6.34 6.34L2.1 2.1m17.9 10h-6m-6 0H2.1m16.12 4.24l4.24 4.24M6.34 17.66l-4.24 4.24"/> 141 + </svg> 142 + </button> 143 + </nav> 144 + 145 + <!-- Account Switcher --> 146 + <div class="flex flex-col gap-2"> 147 + <button class="w-10 h-10 rounded-full overflow-hidden border-2 border-transparent hover:border-white/20 transition-colors"> 148 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Current account" class="w-full h-full object-cover"> 149 + </button> 150 + <button class="w-10 h-10 rounded-full border border-white/10 flex items-center justify-center text-xs text-white/40 hover:text-white/60 hover:bg-white/5 transition-all"> 151 + +1 152 + </button> 153 + </div> 154 + </aside> 155 + 156 + <!-- Main Content --> 157 + <main class="flex-1 ml-16 mr-80"> 158 + <!-- Header --> 159 + <header class="sticky top-0 z-40 panel backdrop-blur-xl border-b border-white/5"> 160 + <div class="flex items-center justify-between px-6 py-4"> 161 + <h1 class="text-xl font-medium" style="letter-spacing: -0.02em;">Timeline</h1> 162 + <span class="text-xs" style="color: var(--on-surface-variant);">Following</span> 163 + </div> 164 + 165 + <!-- Feed Tabs --> 166 + <div class="flex px-6 border-t border-white/5"> 167 + <button class="feed-tab active px-4 py-3 text-sm font-medium">Following</button> 168 + <button class="feed-tab px-4 py-3 text-sm font-medium">Discover</button> 169 + <button class="feed-tab px-4 py-3 text-sm font-medium">Feeds</button> 170 + <button class="feed-tab px-4 py-3 text-sm font-medium">Lists</button> 171 + </div> 172 + </header> 173 + 174 + <!-- New Post Composer (Compact) --> 175 + <div class="px-6 py-4 border-b border-white/5"> 176 + <div class="flex gap-3"> 177 + <div class="w-10 h-10 rounded-full overflow-hidden shrink-0"> 178 + <img src="https://placehold.co/40x40/7dafff/05080f?text=U" alt="Your avatar" class="w-full h-full object-cover"> 179 + </div> 180 + <div class="flex-1"> 181 + <input type="text" 182 + placeholder="What's happening?" 183 + class="w-full bg-transparent text-sm outline-none placeholder-white/30" 184 + style="color: var(--on-surface);"> 185 + <div class="flex items-center justify-between mt-3"> 186 + <div class="flex gap-1"> 187 + <button class="icon-btn p-2 rounded-lg text-white/40 transition-all"> 188 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 189 + <rect x="3" y="3" width="18" height="18" rx="2"/> 190 + <circle cx="8.5" cy="8.5" r="1.5"/> 191 + <path d="M21 15l-5-5L5 21"/> 192 + </svg> 193 + </button> 194 + <button class="icon-btn p-2 rounded-lg text-white/40 transition-all"> 195 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 196 + <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/> 197 + <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/> 198 + </svg> 199 + </button> 200 + <button class="icon-btn p-2 rounded-lg text-white/40 transition-all"> 201 + <svg width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 202 + <circle cx="12" cy="12" r="10"/> 203 + <path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01"/> 204 + </svg> 205 + </button> 206 + </div> 207 + <button class="gradient-btn px-6 py-2 rounded-full text-sm font-medium">Post</button> 208 + </div> 209 + </div> 210 + </div> 211 + </div> 212 + 213 + <!-- Feed --> 214 + <div class="divide-y divide-white/5"> 215 + <!-- Post 1 --> 216 + <article class="post-card px-6 py-5 transition-colors"> 217 + <div class="flex gap-3"> 218 + <div class="w-11 h-11 rounded-full overflow-hidden shrink-0 cursor-pointer"> 219 + <img src="https://placehold.co/44x44/333/fff?text=A" alt="Author" class="w-full h-full object-cover"> 220 + </div> 221 + <div class="flex-1 min-w-0"> 222 + <div class="flex items-center gap-2 mb-1"> 223 + <span class="font-medium text-sm">Alice Chen</span> 224 + <span class="text-xs" style="color: var(--on-surface-variant);">@alice.bsky.social</span> 225 + <span class="text-xs" style="color: var(--on-surface-variant);">· 2h</span> 226 + </div> 227 + <p class="text-sm leading-relaxed mb-3" style="color: var(--on-secondary-container);"> 228 + Just published a new article about the AT Protocol's architecture and how it enables truly decentralized social media. The implications for data ownership are massive! 🧵 229 + </p> 230 + <div class="flex items-center gap-6"> 231 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 232 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 233 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 234 + </svg> 235 + <span>12</span> 236 + </button> 237 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 238 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 239 + <path d="M7 10v12l4-4 4 4V10"/> 240 + <path d="M5 10a7 7 0 0 1 14 0"/> 241 + </svg> 242 + <span>48</span> 243 + </button> 244 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 245 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 246 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 247 + <polyline points="16 6 12 2 8 6"/> 248 + <line x1="12" y1="2" x2="12" y2="15"/> 249 + </svg> 250 + <span>Share</span> 251 + </button> 252 + </div> 253 + </div> 254 + </div> 255 + </article> 256 + 257 + <!-- Post 2 (With Image) --> 258 + <article class="post-card px-6 py-5 transition-colors"> 259 + <div class="flex gap-3"> 260 + <div class="w-11 h-11 rounded-full overflow-hidden shrink-0"> 261 + <img src="https://placehold.co/44x44/555/fff?text=B" alt="Author" class="w-full h-full object-cover"> 262 + </div> 263 + <div class="flex-1 min-w-0"> 264 + <div class="flex items-center gap-2 mb-1"> 265 + <span class="font-medium text-sm">Bob Martinez</span> 266 + <span class="text-xs" style="color: var(--on-surface-variant);">@bob.dev</span> 267 + <span class="text-xs" style="color: var(--on-surface-variant);">· 4h</span> 268 + </div> 269 + <p class="text-sm leading-relaxed mb-3" style="color: var(--on-secondary-container);"> 270 + Working on a new Rust project for handling AT Protocol lexicons. The type generation is getting really clean! 271 + </p> 272 + <div class="rounded-2xl overflow-hidden mb-3 border border-white/5" style="max-height: 300px;"> 273 + <img src="https://placehold.co/600x300/1a1a1a/7dafff?text=Code+Preview" alt="Code screenshot" class="w-full h-full object-cover"> 274 + </div> 275 + <div class="flex items-center gap-6"> 276 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 277 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 278 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 279 + </svg> 280 + <span>8</span> 281 + </button> 282 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--primary);"> 283 + <svg width="16" height="16" fill="currentColor" viewBox="0 0 24 24"> 284 + <path d="M7 10v12l4-4 4 4V10"/> 285 + <path d="M5 10a7 7 0 0 1 14 0"/> 286 + </svg> 287 + <span>156</span> 288 + </button> 289 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 290 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 291 + <path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/> 292 + <polyline points="16 6 12 2 8 6"/> 293 + <line x1="12" y1="2" x2="12" y2="15"/> 294 + </svg> 295 + <span>Share</span> 296 + </button> 297 + </div> 298 + </div> 299 + </div> 300 + </article> 301 + 302 + <!-- Post 3 (Repost) --> 303 + <article class="post-card px-6 py-5 transition-colors"> 304 + <div class="flex gap-3"> 305 + <div class="w-11 h-11 rounded-full overflow-hidden shrink-0"> 306 + <img src="https://placehold.co/44x44/666/fff?text=C" alt="Author" class="w-full h-full object-cover"> 307 + </div> 308 + <div class="flex-1 min-w-0"> 309 + <div class="flex items-center gap-1.5 mb-2"> 310 + <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" style="color: var(--primary);"> 311 + <path d="M17 1l4 4-4 4"/> 312 + <path d="M3 11V9a4 4 0 0 1 4-4h14"/> 313 + <path d="M7 23l-4-4 4-4"/> 314 + <path d="M21 13v2a4 4 0 0 1-4 4H3"/> 315 + </svg> 316 + <span class="text-xs font-medium" style="color: var(--primary);">Carol reposted</span> 317 + </div> 318 + <div class="flex items-center gap-2 mb-1"> 319 + <span class="font-medium text-sm">David Kim</span> 320 + <span class="text-xs" style="color: var(--on-surface-variant);">@david.kim</span> 321 + <span class="text-xs" style="color: var(--on-surface-variant);">· 6h</span> 322 + </div> 323 + <p class="text-sm leading-relaxed mb-3" style="color: var(--on-secondary-container);"> 324 + The future of social media is interoperable. Protocols over platforms every time. 325 + </p> 326 + <div class="panel rounded-xl p-4 border border-white/5"> 327 + <p class="text-xs" style="color: var(--on-surface-variant);">Quoted post from @protocol.fan</p> 328 + </div> 329 + <div class="flex items-center gap-6 mt-3"> 330 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 331 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 332 + <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> 333 + </svg> 334 + <span>5</span> 335 + </button> 336 + <button class="flex items-center gap-1.5 text-xs icon-btn p-1.5 rounded-lg transition-all" style="color: var(--on-surface-variant);"> 337 + <svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"> 338 + <path d="M7 10v12l4-4 4 4V10"/> 339 + <path d="M5 10a7 7 0 0 1 14 0"/> 340 + </svg> 341 + <span>23</span> 342 + </button> 343 + </div> 344 + </div> 345 + </div> 346 + </article> 347 + </div> 348 + </main> 349 + 350 + <!-- Right Sidebar --> 351 + <aside class="fixed right-0 top-0 w-80 h-full panel border-l border-white/5 overflow-y-auto"> 352 + <!-- Search --> 353 + <div class="p-4 border-b border-white/5"> 354 + <div class="relative"> 355 + <svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" style="color: var(--on-surface-variant);"> 356 + <circle cx="11" cy="11" r="8"/> 357 + <path d="m21 21-4.35-4.35"/> 358 + </svg> 359 + <input type="text" 360 + placeholder="Search" 361 + class="w-full pl-10 pr-4 py-2.5 rounded-full text-sm outline-none transition-all" 362 + style="background: rgba(0,0,0,0.4); color: var(--on-surface);"> 363 + </div> 364 + </div> 365 + 366 + <!-- Trending --> 367 + <div class="p-4 border-b border-white/5"> 368 + <h3 class="text-sm font-medium mb-3">Trending</h3> 369 + <div class="space-y-3"> 370 + <div class="group cursor-pointer"> 371 + <p class="text-xs" style="color: var(--on-surface-variant);">Technology · Trending</p> 372 + <p class="text-sm font-medium">AT Protocol</p> 373 + <p class="text-xs" style="color: var(--on-surface-variant);">12.5K posts</p> 374 + </div> 375 + <div class="group cursor-pointer"> 376 + <p class="text-xs" style="color: var(--on-surface-variant);">Development · Trending</p> 377 + <p class="text-sm font-medium">Rust</p> 378 + <p class="text-xs" style="color: var(--on-surface-variant);">8.2K posts</p> 379 + </div> 380 + </div> 381 + </div> 382 + 383 + <!-- Who to follow --> 384 + <div class="p-4"> 385 + <h3 class="text-sm font-medium mb-3">Who to follow</h3> 386 + <div class="space-y-3"> 387 + <div class="flex items-center gap-3"> 388 + <div class="w-10 h-10 rounded-full overflow-hidden"> 389 + <img src="https://placehold.co/40x40/444/fff?text=E" alt="User" class="w-full h-full object-cover"> 390 + </div> 391 + <div class="flex-1 min-w-0"> 392 + <p class="text-sm font-medium truncate">Emma Watson</p> 393 + <p class="text-xs truncate" style="color: var(--on-surface-variant);">@emma.codes</p> 394 + </div> 395 + <button class="gradient-btn px-4 py-1.5 rounded-full text-xs font-medium">Follow</button> 396 + </div> 397 + <div class="flex items-center gap-3"> 398 + <div class="w-10 h-10 rounded-full overflow-hidden"> 399 + <img src="https://placehold.co/40x40/555/fff?text=F" alt="User" class="w-full h-full object-cover"> 400 + </div> 401 + <div class="flex-1 min-w-0"> 402 + <p class="text-sm font-medium truncate">Frank Ocean</p> 403 + <p class="text-xs truncate" style="color: var(--on-surface-variant);">@frank.o</p> 404 + </div> 405 + <button class="gradient-btn px-4 py-1.5 rounded-full text-xs font-medium">Follow</button> 406 + </div> 407 + </div> 408 + </div> 409 + </aside> 410 + 411 + </body> 412 + </html>
+95
docs/specs/feeds.md
··· 1 + # Feeds & Social Features 2 + 3 + ## Feed-Centric Architecture 4 + 5 + Feeds are the primary view. The "Following" timeline is one feed among many — custom feed generators and lists sit alongside it as equal peers. Pinned feeds render as switchable tabs; saved (unpinned) feeds live in a drawer for quick access. 6 + 7 + ### User Preferences & Feed Discovery 8 + 9 + | Action | Endpoint | 10 + | ------------------- | ---------------------------------------------------- | 11 + | Get saved/pinned | `app.bsky.actor.getPreferences` → `savedFeedsPrefV2` | 12 + | Update saved/pinned | `app.bsky.actor.putPreferences` | 13 + | Hydrate generators | `app.bsky.feed.getFeedGenerators` (batch by URI) | 14 + | Suggested feeds | `app.bsky.feed.getSuggestedFeeds` | 15 + | Actor's feeds | `app.bsky.feed.getActorFeeds` | 16 + 17 + #### `savedFeedsPrefV2` Shape 18 + 19 + Each saved feed: 20 + 21 + ```ts 22 + { id: string, type: "timeline" | "feed" | "list", value: string, pinned: boolean } 23 + ``` 24 + 25 + - `"timeline"` → value `"following"`, loaded via `getTimeline` 26 + - `"feed"` → value is an `at://` URI, loaded via `getFeed` 27 + - `"list"` → value is an `at://` URI, loaded via `app.bsky.feed.getListFeed` 28 + 29 + Pinned feeds appear in array order as tabs. 30 + 31 + #### Per-Feed Display Preferences (`feedViewPref`) 32 + 33 + ```ts 34 + { feed: string, hideReplies?: boolean, hideRepliesByUnfollowed?: boolean, 35 + hideRepliesByLikeCount?: number, hideReposts?: boolean, hideQuotePosts?: boolean } 36 + ``` 37 + 38 + ### Feed Content (XRPC via jacquard) 39 + 40 + | Action | Lexicon | 41 + | ------------------ | --------------------------------------------------------- | 42 + | Following timeline | `app.bsky.feed.getTimeline` | 43 + | Custom feed | `app.bsky.feed.getFeed` | 44 + | List feed | `app.bsky.feed.getListFeed` | 45 + | Author feed | `app.bsky.feed.getAuthorFeed` | 46 + | Post thread | `app.bsky.feed.getPostThread` | 47 + | Like a post | `app.bsky.feed.like` (create record) | 48 + | Repost | `app.bsky.feed.repost` (create record) | 49 + | Create post | `com.atproto.repo.createRecord` with `app.bsky.feed.post` | 50 + | Get likes list | `app.bsky.feed.getActorLikes` | 51 + | Get profile | `app.bsky.actor.getProfile` | 52 + | Follow/unfollow | `app.bsky.graph.follow` (create/delete record) | 53 + | Mute/block | `app.bsky.graph.muteActor` / `app.bsky.graph.block` | 54 + 55 + ### Client Flow 56 + 57 + 1. On login / account switch, call `getPreferences` → extract `savedFeedsPrefV2` 58 + 2. Filter pinned feeds → render as tabs (ordered by array position) 59 + 3. Call `getFeedGenerators` with pinned feed URIs → hydrate display names + avatars for tab labels 60 + 4. Active tab loads content: `getTimeline` for timeline type, `getFeed` for feed type, `getListFeed` for list type 61 + 5. Saved (unpinned) feeds accessible via feeds drawer 62 + 63 + ## Post Composer 64 + 65 + - Rich text via `jacquard::richtext` — auto-detect mentions, links, hashtags 66 + - Image/media upload via `com.atproto.repo.uploadBlob` 67 + - Reply threading with parent/root refs 68 + - Quote post embed 69 + 70 + ## Keyboard Shortcuts 71 + 72 + | Key | Action | 73 + | ------------- | --------------------------- | 74 + | `n` | New post (open composer) | 75 + | `j` / `k` | Next / previous post | 76 + | `l` | Like focused post | 77 + | `r` | Reply to focused post | 78 + | `t` | Repost focused post | 79 + | `o` / `Enter` | Open thread | 80 + | `1`–`9` | Switch between pinned feeds | 81 + 82 + ## UX Polish 83 + 84 + - New posts slide in from top via `Motion` with spring easing; scroll position preserved 85 + - Like/repost actions: `Motion` scale pop on the icon (1.0 -> 1.3 -> 1.0) 86 + - Post card: subtle `Motion` fade-in on viewport enter during infinite scroll 87 + - Composer: `Presence` slide-up animation on open, slide-down on dismiss 88 + - Feed tab switch: `Presence` crossfade between feed content 89 + - Skeleton screens while feeds load; error toast with retry button on network failure 90 + - Per-feed display preferences (hide reposts/replies/quotes) stored via `putPreferences` 91 + 92 + ## Direct Messages 93 + 94 + - `chat.bsky.convo.*` lexicons for DM support 95 + - Deferred to post-MVP unless trivial to add
+1 -1
docs/specs/mvp.md
··· 52 52 Details in sub-specs: 53 53 54 54 - [Authentication & Accounts](./auth.md) 55 - - [Timeline & Social](./timeline.md) 55 + - [Feeds & Social](./feeds.md) 56 56 - [AT Explorer](./explorer.md) 57 57 - [Search & Embeddings](./search.md) 58 58 - [standard.site Integration](./standard-site.md)
-66
docs/specs/timeline.md
··· 1 - # Timeline & Social Features 2 - 3 - ## Timeline View 4 - 5 - Inspired by Aeronaut's timeline continuity — new posts prepend without losing scroll position. 6 - 7 - ### XRPC Endpoints (via jacquard) 8 - 9 - | Action | Lexicon | 10 - | ------------------ | --------------------------------------------------------- | 11 - | Following timeline | `app.bsky.feed.getTimeline` | 12 - | Custom feed | `app.bsky.feed.getFeed` | 13 - | Author feed | `app.bsky.feed.getAuthorFeed` | 14 - | Post thread | `app.bsky.feed.getPostThread` | 15 - | Like a post | `app.bsky.feed.like` (create record) | 16 - | Repost | `app.bsky.feed.repost` (create record) | 17 - | Create post | `com.atproto.repo.createRecord` with `app.bsky.feed.post` | 18 - | Get likes list | `app.bsky.feed.getActorLikes` | 19 - | Get profile | `app.bsky.actor.getProfile` | 20 - | Follow/unfollow | `app.bsky.graph.follow` (create/delete record) | 21 - | Mute/block | `app.bsky.graph.muteActor` / `app.bsky.graph.block` | 22 - 23 - ### Feed Preferences 24 - 25 - - Toggle reposts, replies, quote-posts per feed (like Aeronaut) 26 - - Store preferences per account in SQLite 27 - 28 - ## Post Composer 29 - 30 - - Rich text via `jacquard::richtext` — auto-detect mentions, links, hashtags 31 - - Image/media upload via `com.atproto.repo.uploadBlob` 32 - - Reply threading with parent/root refs 33 - - Quote post embed 34 - 35 - ## Notifications 36 - 37 - - `app.bsky.notification.listNotifications` — poll or use Jetstream 38 - - Separate tabs: Mentions vs Activity (Aeronaut pattern) 39 - - System notifications via Tauri 40 - 41 - ## Keyboard Shortcuts 42 - 43 - | Key | Action | 44 - | ------------- | ------------------------ | 45 - | `n` | New post (open composer) | 46 - | `j` / `k` | Next / previous post | 47 - | `l` | Like focused post | 48 - | `r` | Reply to focused post | 49 - | `t` | Repost focused post | 50 - | `o` / `Enter` | Open thread | 51 - | `1`–`9` | Switch between feeds | 52 - 53 - ## UX Polish 54 - 55 - - New posts slide in from top via `Motion` with spring easing; scroll position preserved 56 - - Like/repost actions: `Motion` scale pop on the icon (1.0 → 1.3 → 1.0) 57 - - Post card: subtle `Motion` fade-in on viewport enter during infinite scroll 58 - - Composer: `Presence` slide-up animation on open, slide-down on dismiss 59 - - Feed switcher: `Presence` crossfade between feed content on tab change 60 - - Skeleton screens while feeds load; error toast with retry button on network failure 61 - - Feed preferences stored per account in SQLite 62 - 63 - ## Direct Messages 64 - 65 - - `chat.bsky.convo.*` lexicons for DM support 66 - - Deferred to post-MVP unless trivial to add
+49
docs/tasks/03-feeds.md
··· 1 + # Task 03: Feeds 2 + 3 + Spec: [feeds.md](../specs/feeds.md) 4 + 5 + ## Steps 6 + 7 + ### Backend — `src-tauri/src/feed.rs` 8 + 9 + - [ ] `get_preferences()` — calls `app.bsky.actor.getPreferences`, extracts `savedFeedsPrefV2` items and `feedViewPref` entries 10 + - [ ] `get_feed_generators(uris: Vec<String>)` — calls `app.bsky.feed.getFeedGenerators` to hydrate display names/avatars 11 + - [ ] `get_timeline(cursor: Option<String>, limit: u32)` — calls `app.bsky.feed.getTimeline` 12 + - [ ] `get_feed(uri: String, cursor: Option<String>, limit: u32)` — calls `app.bsky.feed.getFeed` for custom feed generators 13 + - [ ] `get_list_feed(uri: String, cursor: Option<String>, limit: u32)` — calls `app.bsky.feed.getListFeed` 14 + - [ ] `get_post_thread(uri: String)` — thread view 15 + - [ ] `get_author_feed(did: String, cursor: Option<String>)` 16 + - [ ] `create_post(text: String, reply_to: Option<ReplyRef>, embed: Option<Embed>)` — with richtext facet detection via `jacquard::richtext` 17 + - [ ] `like_post(uri: String, cid: String)` / `unlike_post(uri: String)` 18 + - [ ] `repost(uri: String, cid: String)` / `unrepost(uri: String)` 19 + 20 + ### Frontend — Feed Tabs & Content 21 + 22 + - [ ] Feed tab bar — pinned feeds as tabs, hydrated with generator display names/avatars; `1`–`9` keyboard shortcuts to switch 23 + - [ ] Feed content loader — dispatches to correct endpoint based on feed type (`timeline` / `feed` / `list`) 24 + - [ ] Infinite scroll with cursor pagination and scroll-position preservation 25 + - [ ] `Presence` crossfade animation on tab switch 26 + - [ ] Skeleton screens while feeds load 27 + 28 + ### Frontend — Post Card & Actions 29 + 30 + - [ ] Post card component (author, text, embeds, timestamps, action bar) — `Motion` fade-in on viewport enter 31 + - [ ] Like/repost icon `Motion` scale pop animation (1.0 -> 1.3 -> 1.0) 32 + - [ ] `j/k` keyboard navigation between posts, `l` like, `r` reply, `t` repost, `o` open thread 33 + 34 + ### Frontend — Thread View 35 + 36 + - [ ] Thread view with nested replies 37 + - [ ] Navigate into thread from post card (`o` / `Enter`) 38 + 39 + ### Frontend — Post Composer 40 + 41 + - [ ] Composer with `Presence` slide-up/down, `n` keyboard shortcut to open 42 + - [ ] Mention/hashtag autocomplete 43 + - [ ] Reply threading with parent/root refs 44 + - [ ] Quote post embed 45 + 46 + ### Frontend — Feed Preferences 47 + 48 + - [ ] Per-feed display toggles (hide reposts/replies/quotes) via `feedViewPref` 49 + - [ ] Feeds drawer for accessing saved (unpinned) feeds
-23
docs/tasks/03-timeline.md
··· 1 - # Task 03: Timeline & Feeds 2 - 3 - Spec: [timeline.md](../specs/timeline.md) 4 - 5 - ## Steps 6 - 7 - - [ ] Create `src-tauri/src/feed.rs` — Tauri commands for feed operations 8 - - [ ] `get_timeline(cursor: Option<String>, limit: u32)` — calls `app.bsky.feed.getTimeline` via jacquard Agent 9 - - [ ] `get_feed(uri: String, cursor: Option<String>)` — custom/list feeds 10 - - [ ] `get_post_thread(uri: String)` — thread view 11 - - [ ] `create_post(text: String, reply_to: Option<ReplyRef>, embed: Option<Embed>)` — with richtext facet detection via `jacquard::richtext` 12 - - [ ] `like_post(uri: String, cid: String)` / `unlike_post(uri: String)` 13 - - [ ] `repost(uri: String, cid: String)` / `unrepost(uri: String)` 14 - - [ ] `get_author_feed(did: String, cursor: Option<String>)` 15 - - [ ] **Frontend**: timeline component with infinite scroll, scroll-position preservation 16 - - [ ] **Frontend**: post card component (text, embeds, actions bar) — `Motion` fade-in on viewport enter 17 - - [ ] **Frontend**: like/repost icon `Motion` scale pop animation (1.0 → 1.3 → 1.0) 18 - - [ ] **Frontend**: post composer with `Presence` slide-up/down, mention/hashtag autocomplete 19 - - [ ] **Frontend**: thread view with nested replies 20 - - [ ] **Frontend**: feed selector (Following, custom feeds, lists) with `Presence` crossfade on switch 21 - - [ ] **Frontend**: feed preferences toggle (hide reposts/replies/quotes), persisted per account 22 - - [ ] **Frontend**: skeleton screens while feeds load 23 - - [ ] **Frontend**: keyboard shortcuts — `n` post, `j/k` navigate, `l` like, `r` reply, `t` repost, `o` open, `1-9` feeds
+1 -1
docs/tasks/04-notifications.md
··· 1 1 # Task 04: Notifications 2 2 3 - Spec: [timeline.md](../specs/timeline.md) 3 + Spec: [feeds.md](../specs/feeds.md) 4 4 5 5 ## Steps 6 6
+1 -1
docs/tasks/mvp.md
··· 9 9 10 10 ## Phase 2: Core Social 11 11 12 - - [Timeline & Feeds](./03-timeline.md) — Feed views, post rendering, composer, keyboard shortcuts, scroll animations 12 + - [Feeds](./03-feeds.md) — Pinned feed tabs, post rendering, composer, keyboard shortcuts, scroll animations 13 13 - [Notifications](./04-notifications.md) �� Mentions, activity, system notifications, badge animations 14 14 15 15 ## Phase 3: Power Features
+3
public/lazurite.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" viewBox="0 0 512 512"> 2 + <path fill="currentColor" d="M128 16v99.3l119 118.9V120.1zm256 0L265 120.1v114.1l119-119zM16 128l104 119h114.2L115.3 128zm380.8 0l-119 119h114.1l104-119zM120 265L16 384h99.2l119-119zm157.8 0l119 119h99.1l-104-119zM247 277.8l-119 119V496l119-104.1zm18 0v114.1L384 496v-99.2z"/> 3 + </svg>