this repo has no description
0
fork

Configure Feed

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

river

+68 -196
+47 -177
stack/river/lib/format.ml
··· 152 152 line-height: 1.5; 153 153 color: #333; 154 154 background: #fff; 155 - display: flex; 156 - flex-direction: column; 157 - } 158 - 159 - .site-container { 160 - display: flex; 161 - max-width: 1200px; 155 + max-width: 900px; 162 156 margin: 0 auto; 163 - width: 100%; 164 - } 165 - 166 - .date-tracker { 167 - position: sticky; 168 - top: 20px; 169 - width: 120px; 170 - padding: 20px 15px; 171 - height: fit-content; 172 - flex-shrink: 0; 173 - } 174 - 175 - .date-tracker-content { 176 - text-align: right; 177 - border-right: 3px solid #e1e4e8; 178 - padding-right: 15px; 179 - } 180 - 181 - .date-year { 182 - font-size: 24px; 183 - font-weight: 700; 184 - color: #0366d6; 185 - margin-bottom: 4px; 186 - } 187 - 188 - .date-month { 189 - font-size: 14px; 190 - font-weight: 600; 191 - color: #586069; 192 - margin-bottom: 2px; 193 - } 194 - 195 - .date-day { 196 - font-size: 13px; 197 - color: #959da5; 198 - } 199 - 200 - .main-content { 201 - flex: 1; 202 - min-width: 0; 203 157 padding: 15px; 204 158 } 205 159 ··· 234 188 color: #0366d6; 235 189 } 236 190 237 - @media (max-width: 768px) { 238 - .site-container { 239 - flex-direction: column; 240 - } 241 - 242 - .date-tracker { 243 - position: relative; 244 - top: 0; 245 - width: 100%; 246 - padding: 10px 15px; 247 - border-bottom: 1px solid #e1e4e8; 248 - } 249 - 250 - .date-tracker-content { 251 - text-align: left; 252 - border-right: none; 253 - border-bottom: 2px solid #e1e4e8; 254 - padding-right: 0; 255 - padding-bottom: 10px; 256 - } 257 - 258 - .main-content { 259 - padding: 15px; 260 - } 261 - } 262 - 263 191 .post { 264 - margin-bottom: 25px; 265 - padding-bottom: 20px; 192 + margin-bottom: 30px; 193 + padding-bottom: 25px; 266 194 border-bottom: 1px solid #e1e4e8; 195 + overflow: hidden; 267 196 } 268 197 269 198 .post:last-child { 270 199 border-bottom: none; 271 200 } 272 201 202 + .author-thumbnail { 203 + float: right; 204 + width: 48px; 205 + height: 48px; 206 + border-radius: 50%; 207 + object-fit: cover; 208 + margin-left: 15px; 209 + margin-bottom: 10px; 210 + border: 2px solid #e1e4e8; 211 + transition: transform 0.2s, box-shadow 0.2s; 212 + } 213 + 214 + .author-thumbnail:hover { 215 + transform: scale(1.05); 216 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); 217 + } 218 + 273 219 .post-title { 274 220 font-size: 18px; 275 221 font-weight: 600; 276 - margin-bottom: 5px; 222 + margin-bottom: 4px; 277 223 line-height: 1.3; 278 224 } 279 225 ··· 286 232 text-decoration: underline; 287 233 } 288 234 289 - .post-meta { 235 + .post-meta-line { 290 236 font-size: 12px; 291 237 color: #586069; 292 - margin-bottom: 8px; 293 - display: flex; 294 - align-items: center; 295 - gap: 8px; 238 + margin-bottom: 10px; 296 239 } 297 240 298 - .post-meta a { 241 + .post-meta-line a { 299 242 color: #586069; 300 243 text-decoration: none; 244 + font-weight: 600; 301 245 } 302 246 303 - .post-meta a:hover { 247 + .post-meta-line a:hover { 304 248 color: #0366d6; 305 249 } 306 250 307 - .author-thumbnail { 308 - width: 48px; 309 - height: 48px; 310 - border-radius: 50%; 311 - object-fit: cover; 312 - flex-shrink: 0; 313 - } 314 - 315 - .post-meta-text { 316 - flex: 1; 317 - display: flex; 318 - flex-direction: column; 319 - justify-content: center; 320 - } 321 - 322 - .author-name { 323 - font-size: 14px; 324 - font-weight: 600; 325 - color: #24292e; 326 - margin-bottom: 2px; 327 - } 328 - 329 - .post-date { 330 - font-size: 12px; 331 - color: #586069; 251 + @media (max-width: 768px) { 252 + .author-thumbnail { 253 + float: none; 254 + display: block; 255 + margin: 0 auto 10px auto; 256 + } 332 257 } 333 258 334 259 .post-excerpt { ··· 851 776 <style>%s</style> 852 777 </head> 853 778 <body> 854 - <div class="site-container"> 855 - <aside class="date-tracker" id="date-tracker"> 856 - <div class="date-tracker-content"> 857 - <div class="date-year" id="current-year">2025</div> 858 - <div class="date-month" id="current-month">January</div> 859 - <div class="date-day" id="current-day">1</div> 860 - </div> 861 - </aside> 862 - <div class="main-content"> 863 - <header> 864 - <h1><a href="index.html">River Feed</a></h1> 865 - <nav> 866 - <a href="index.html"%s>Posts</a> 867 - <a href="authors/index.html"%s>Authors</a> 868 - <a href="categories/index.html"%s>Categories</a> 869 - <a href="links.html"%s>Links</a> 870 - </nav> 871 - </header> 872 - <main> 779 + <header> 780 + <h1><a href="index.html">River Feed</a></h1> 781 + <nav> 782 + <a href="index.html"%s>Posts</a> 783 + <a href="authors/index.html"%s>Authors</a> 784 + <a href="categories/index.html"%s>Categories</a> 785 + <a href="links.html"%s>Links</a> 786 + </nav> 787 + </header> 788 + <main> 873 789 %s 874 - </main> 875 - <footer> 876 - Generated by River Feed Aggregator 877 - </footer> 878 - </div> 879 - </div> 790 + </main> 791 + <footer> 792 + Generated by River Feed Aggregator 793 + </footer> 880 794 <div class="lightbox" id="lightbox"> 881 795 <img id="lightbox-img" src="" alt=""> 882 796 </div> ··· 930 844 } 931 845 } 932 846 }); 933 - 934 - // Date tracker scroll update 935 - const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 936 - 'July', 'August', 'September', 'October', 'November', 'December']; 937 - 938 - function updateDateTracker() { 939 - const posts = document.querySelectorAll('.post[data-date]'); 940 - if (posts.length === 0) return; 941 - 942 - // Find the post currently in the middle of the viewport 943 - const viewportMiddle = window.scrollY + (window.innerHeight / 2); 944 - let closestPost = posts[0]; 945 - let closestDistance = Math.abs(posts[0].offsetTop - viewportMiddle); 946 - 947 - posts.forEach(post => { 948 - const distance = Math.abs(post.offsetTop - viewportMiddle); 949 - if (distance < closestDistance) { 950 - closestDistance = distance; 951 - closestPost = post; 952 - } 953 - }); 954 - 955 - const dateStr = closestPost.getAttribute('data-date'); 956 - if (dateStr) { 957 - const date = new Date(dateStr); 958 - const yearEl = document.getElementById('current-year'); 959 - const monthEl = document.getElementById('current-month'); 960 - const dayEl = document.getElementById('current-day'); 961 - 962 - if (yearEl) yearEl.textContent = date.getFullYear(); 963 - if (monthEl) monthEl.textContent = monthNames[date.getMonth()]; 964 - if (dayEl) dayEl.textContent = date.getDate(); 965 - } 966 - } 967 - 968 - // Update on scroll and initial load 969 - let scrollTimeout; 970 - window.addEventListener('scroll', function() { 971 - clearTimeout(scrollTimeout); 972 - scrollTimeout = setTimeout(updateDateTracker, 50); 973 - }); 974 - 975 - // Initial update 976 - updateDateTracker(); 977 847 })(); 978 848 </script> 979 849 </body>
+21 -19
stack/river/lib/state.ml
··· 563 563 i >= start_idx && i < start_idx + posts_per_page 564 564 ) html_data in 565 565 566 - let post_htmls = List.map (fun (username, title, author, date, link, content, tags) -> 567 - Log.debug (fun m -> m " Processing post: %s by %s (@%s)" title author username); 568 - (* Create a temporary Post-like structure for rendering *) 569 - (* We'll need to adapt this since we're working with Atom entries *) 566 + let post_htmls = List.map (fun (username, title, _feed_author, date, link, content, tags) -> 567 + Log.debug (fun m -> m " Processing post: %s by @%s" title username); 568 + 569 + (* Get author name from Sortal, fallback to username *) 570 + let author_name = match Sortal.lookup state.sortal username with 571 + | Some contact -> Sortal.Contact.name contact 572 + | None -> username 573 + in 574 + 570 575 let post_html = 571 576 let date_str = Format.Html.format_date date in 572 - (* Format date for data attribute (ISO 8601) *) 573 - let date_iso = Ptime.to_rfc3339 date in 574 577 let link_html = match link with 575 578 | Some uri -> 576 579 Printf.sprintf {|<a href="%s">%s</a>|} ··· 593 596 in 594 597 let thumbnail_html = match get_author_thumbnail username with 595 598 | Some thumb_path -> 596 - Printf.sprintf {|<img src="%s" alt="%s" class="author-thumbnail">|} 599 + Printf.sprintf {|<a href="authors/%s.html"><img src="%s" alt="%s" class="author-thumbnail"></a>|} 600 + (Format.Html.html_escape (sanitize_filename username)) 597 601 (Format.Html.html_escape thumb_path) 598 - (Format.Html.html_escape author) 599 - | None -> "" 602 + (Format.Html.html_escape author_name) 603 + | None -> 604 + Printf.sprintf {|<a href="authors/%s.html"><div class="author-thumbnail" style="background: linear-gradient(135deg, #667eea 0%%, #764ba2 100%%); color: white; display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: 700;">%s</div></a>|} 605 + (Format.Html.html_escape (sanitize_filename username)) 606 + (String.uppercase_ascii (String.sub author_name 0 1)) 600 607 in 601 - Printf.sprintf {|<article class="post" data-date="%s"> 608 + Printf.sprintf {|<article class="post"> 609 + %s 602 610 <h2 class="post-title">%s</h2> 603 - <div class="post-meta"> 604 - %s<div class="post-meta-text"> 605 - <div class="author-name"><a href="authors/%s.html">%s</a></div> 606 - <div class="post-date">%s</div> 607 - </div> 608 - </div> 611 + <div class="post-meta-line">By <a href="authors/%s.html">%s</a> · %s</div> 609 612 <div class="post-excerpt"> 610 613 %s 611 614 </div> ··· 615 618 <a href="#" class="read-more">Read more</a> 616 619 %s 617 620 </article>|} 618 - date_iso 621 + thumbnail_html 619 622 link_html 620 - thumbnail_html 621 623 (Format.Html.html_escape (sanitize_filename username)) 622 - (Format.Html.html_escape author) 624 + (Format.Html.html_escape author_name) 623 625 date_str 624 626 excerpt 625 627 full_content