From 83ab88264880b13e2d0826ee0af8a6f17b18c33e Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Fri, 10 Jan 2025 20:37:49 +0000 Subject: [PATCH 1/9] Feat: Consolidate/port Card and Compact media viewers --- src/mixins/post.pug | 93 +++++++++++++++++-------------------------- src/public/styles.css | 70 +++++++++++++++----------------- 2 files changed, 69 insertions(+), 94 deletions(-) diff --git a/src/mixins/post.pug b/src/mixins/post.pug index c4f3bc5..30310a8 100644 --- a/src/mixins/post.pug +++ b/src/mixins/post.pug @@ -28,77 +28,58 @@ mixin post(p, currentUrl) |  ·  a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩ if (query.view == "card" && !isPostGallery(p) && !isPostImage(p) && !isPostVideo(p) && p.selftext_html) - div.self-text-overflow(class='card') - if query.view == "card" && (p.spoiler || p.over_18) + div.self-text-overflow.card + if p.spoiler || p.over_18 div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) h2 != p.over_18 ? 'nsfw' : 'spoiler' - div.self-text(class='card') + div.self-text.card != convertInlineImageLinks(p.selftext_html) - div.media-preview(class=`${query.view}`) - - var onclick = query.view != "card" ? `toggleDetails('${p.id}')` : `` + if query.view != "card" + div.media-preview + - var onclick = `toggleDetails('${p.id}')` + if isPostGallery(p) + - var item = (p.over_18 ? `/nsfw.svg` : p.spoiler ? `/spoiler.svg` : postGalleryItems(p)[0].url) + img(src=item onclick=onclick) + else if isPostImage(p) + - var url = postThumbnail(p) + img(src=url onclick=onclick) + else if isPostVideo(p) + - var decodedVideos = decodePostVideoUrls(p) + video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") + // Scrubber + source(src=decodedVideos[3]) + else if isPostLink(p) + a(href=p.url) + | ↗ + + details(id=`${p.id}` open=(query.view == "card" && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)))) + summary.expand-post expand media + div.image-viewer if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)) div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) h2 != p.over_18 ? 'nsfw' : 'spoiler' if isPostGallery(p) - - var item = postGalleryItems(p)[0] - if query.view == "card" - div.gallery(class=`${query.view}`) - each item in postGalleryItems(p) - div.gallery-item(class=`${query.view}`) - a(href=`/media/${item.url}`) - img(src=item.url loading="lazy") - div.gallery-item-idx(class=`${query.view}`) - | #{`${item.idx}/${item.total}`} - else - img(src=item.url onclick=onclick) + div.gallery + each item in postGalleryItems(p) + div.gallery-item + a(href=`/media/${item.url}`) + img(src=item.url loading="lazy") + div.gallery-item-idx + | #{`${item.idx}/${item.total}`} else if isPostImage(p) - - var url = query.view == "card" ? p.url : postThumbnail(p) - #{query.view == "card" ? "a href=/media/" + url : span} - img(src=url onclick=onclick) + a(href=`/media/${p.url}`) + img(src=p.url loading="lazy") else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - if query.view == "card" - video(controls="" muted="" data-dashjs-player="" preload="metadata" poster=decodedVideos[4]) + video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy") // HLS source(src=decodedVideos[0]) // Dash source(src=decodedVideos[1]) // Fallback source(src=decodedVideos[2]) - else - video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` width="100px" height="100px") - // Scrubber - source(src=decodedVideos[3]) - else if isPostLink(p) - a(href=p.url) - if (query.view == 'card') - | #{p.domain} - | ↗ - - if query.view == "compact" && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)) - details(id=`${p.id}`) - summary.expand-post expand media - div.image-viewer - if isPostGallery(p) - div.gallery - each item in postGalleryItems(p) - div.gallery-item - div.gallery-item-idx - | #{`${item.idx}/${item.total}`} - a(href=`/media/${item.url}`) - img(src=item.url loading="lazy") - else if isPostImage(p) - a(href=`/media/${p.url}`) - img(src=p.url loading="lazy").post-media - else if isPostVideo(p) - video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy").post-media - //HLS - source(src=decodedVideos[0]) - // Dash - source(src=decodedVideos[1]) - // Fallback - source(src=decodedVideos[2]) - button(onclick=`toggleDetails('${p.id}')`) - | close + if (query.view == "compact") + button(onclick=`toggleDetails('${p.id}')`) + | close diff --git a/src/public/styles.css b/src/public/styles.css index d9f6a40..8bfdc72 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -11,6 +11,9 @@ --link-visited-color: #999; --accent: var(--link-color); --error-text-color: red; + --border-radius-card: 8px; + --border-radius-media: 6px; + --border-radius-preview: 4px; font-family: Inter, sans-serif; font-feature-settings: 'ss01' 1, 'kern' 1, 'liga' 1, 'cv05' 1, 'dlig' 1, 'ss01' 1, 'ss07' 1, 'ss08' 1; @@ -47,10 +50,6 @@ body { color: var(--text-color); } -body:has(details.card[open]) { - overflow: hidden; -} - body.media-maximized { /* Fix for Safari User Agent stylesheet */ margin: 0; @@ -169,7 +168,7 @@ nav { .post-container.card { border: 1px solid var(--bg-color-muted); - border-radius: 8px; + border-radius: var(--border-radius-card); display: block; } @@ -198,37 +197,22 @@ nav { text-overflow: ellipsis; } -.media-preview.card { +.image-viewer { position: relative; padding: 0.3rem; padding-bottom: 0.3rem; } -.media-preview.card > img { +.image-viewer > img { cursor: pointer; } -.gallery.card { - align-items: center; - scroll-snap-type: both mandatory; -} - -.gallery-item.card { - max-width: 100%; - width: 100%; - scroll-snap-align: center; -} - -.gallery-item-idx.card { - text-align: center; -} - .spoiler { background-color: rbga(var(--bg-color-muted), 0.2); /* Safari on iOS <= 17 */ -webkit-backdrop-filter: blur(3rem); backdrop-filter: blur(3rem); - border-radius: 4px; + border-radius: var(--border-radius-preview); position: absolute; top: 0; @@ -247,7 +231,7 @@ nav { z-index: 10; } -.gallery-item-idx.card, +.gallery-item-idx, .spoiler > h2 { text-shadow: 0.1rem 0.1rem 1rem var(--bg-color-muted); } @@ -294,15 +278,16 @@ summary::before { object-fit: cover; width: 4rem; height: 4rem; + border-radius: var(--border-radius-preview); } -.media-preview.card { +.image-viewer { padding: unset; } -.media-preview.card img, -.media-preview.card video { - border-radius: 6px; +.image-viewer img, +.image-viewer video { + border-radius: var(--border-radius-media); max-height: 40vh; max-width: 95%; @@ -317,13 +302,13 @@ summary::before { object-fit: fill; } -.media-preview.card a { +.image-viewer a { font-size: 1.5rem; padding: unset; padding-left: 1rem; } -.media-preview.card a:has(img) { +.image-viewer a:has(img) { font-size: 0rem; padding: unset; } @@ -382,12 +367,12 @@ form { width: 5rem; height: 5rem; } - .media-preview.card img, - .media-preview.card video + .image-viewer img, + .image-viewer video { max-height: 50vh; } - .media-preview.card a { + .image-viewer a { font-size: 1rem; margin: 0.7rem; padding: initial; @@ -424,8 +409,8 @@ form { width: 5rem; height: 5rem; } - .media-preview.card img, - .media-preview.card video + .image-viewer img, + .image-viewer video { max-height: 30vh; } @@ -433,7 +418,7 @@ form { font-size: 2rem; padding: 2rem; } - .media-preview.card a { + .image-viewer a { font-size: 1rem; margin: 0.5rem; padding: initial; @@ -464,8 +449,8 @@ form { flex: 1 1 40%; width: 40%; } - .media-preview.card img, - .media-preview.card video + .image-viewer img, + .image-viewer video { max-height: 20vh; } @@ -648,11 +633,20 @@ a { overflow-x: auto; position: relative; padding: 5px; + align-items: center; + scroll-snap-type: both mandatory; } .gallery-item { flex: 0 0 auto; margin-right: 10px; + max-width: 100%; + width: 100%; + scroll-snap-align: center; +} + +.gallery-item-idx { + text-align: center; } .gallery img { From 569b3281377e12578b8dcc742e7925b2e2e6950d Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Fri, 10 Jan 2025 22:58:57 +0000 Subject: [PATCH 2/9] Split `post-container` class Fixes `flex-*` issues in consolidated views. --- src/mixins/post.pug | 142 ++++++++++++++++++++++-------------------- src/public/styles.css | 20 +++--- 2 files changed, 84 insertions(+), 78 deletions(-) diff --git a/src/mixins/post.pug b/src/mixins/post.pug index 30310a8..4ec7614 100644 --- a/src/mixins/post.pug +++ b/src/mixins/post.pug @@ -6,80 +6,84 @@ mixin post(p, currentUrl) - var sortQuery = query && query.sort ? query.sort + (query.t ? '&t=' + query.t : '') : 'hot' article(class=`post`) div.post-container(class=`${query.view} ${p.stickied?"sticky":""}`) - div.post-text(class=`${query.view}`) - div.title-container(class=`${query.view}`) - a(class=`${query.view}`, href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) - != p.title - span.domain (#{p.domain}) - div.info-container - p - | #{fmtnum(p.ups)} ↑ - if p.gilded > 0 + div.post-info + div.post-text(class=`${query.view}`) + div.title-container(class=`${query.view}`) + a(class=`${query.view}`, href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) + != p.title + span.domain (#{p.domain}) + div.info-container + p + | #{fmtnum(p.ups)} ↑ + if p.gilded > 0 + |  ·  + span.gilded + | #{p.gilded} ☆ + span.post-author + |  ·  + | u/#{p.author} |  ·  - span.gilded - | #{p.gilded} ☆ - span.post-author + | #{timeDifference(Date.now(), p.created * 1000)} |  ·  - | u/#{p.author} - |  ·  - | #{timeDifference(Date.now(), p.created * 1000)} - |  ·  - a(href=`/r/${p.subreddit}?sort=${sortQuery}&view=${viewQuery}`) r/#{p.subreddit} - |  ·  - a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩ - if (query.view == "card" && !isPostGallery(p) && !isPostImage(p) && !isPostVideo(p) && p.selftext_html) - div.self-text-overflow.card - if p.spoiler || p.over_18 - div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) - h2 - != p.over_18 ? 'nsfw' : 'spoiler' - div.self-text.card - != convertInlineImageLinks(p.selftext_html) - if query.view != "card" - div.media-preview - - var onclick = `toggleDetails('${p.id}')` + a(href=`/r/${p.subreddit}?sort=${sortQuery}&view=${viewQuery}`) r/#{p.subreddit} + |  ·  + a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩ + if (query.view == "card" && !isPostGallery(p) && !isPostImage(p) && !isPostVideo(p) && p.selftext_html) + div.self-text-overflow.card + if p.spoiler || p.over_18 + div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) + h2 + != p.over_18 ? 'nsfw' : 'spoiler' + div.self-text.card + != convertInlineImageLinks(p.selftext_html) + if query.view != "card" + div.media-preview + - var onclick = `toggleDetails('${p.id}')` + if isPostGallery(p) + - var item = (p.over_18 ? `/nsfw.svg` : p.spoiler ? `/spoiler.svg` : postGalleryItems(p)[0].url) + img(src=item onclick=onclick) + else if isPostImage(p) + - var url = postThumbnail(p) + img(src=url onclick=onclick) + else if isPostVideo(p) + - var decodedVideos = decodePostVideoUrls(p) + video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") + // Scrubber + source(src=decodedVideos[3]) + else if isPostLink(p) + a(href=p.url) + | ↗ + + details(id=`${p.id}` open=(query.view == "card") class=`${query.view}`) + summary.expand-post expand media + div.image-viewer + if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)) + div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) + h2 + != p.over_18 ? 'nsfw' : 'spoiler' if isPostGallery(p) - - var item = (p.over_18 ? `/nsfw.svg` : p.spoiler ? `/spoiler.svg` : postGalleryItems(p)[0].url) - img(src=item onclick=onclick) + div.gallery + each item in postGalleryItems(p) + div.gallery-item + a(href=`/media/${item.url}`) + img(src=item.url loading="lazy") + div.gallery-item-idx + | #{`${item.idx}/${item.total}`} else if isPostImage(p) - - var url = postThumbnail(p) - img(src=url onclick=onclick) + a(href=`/media/${p.url}`) + img(src=p.url loading="lazy") else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") - // Scrubber - source(src=decodedVideos[3]) + video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy") + // HLS + source(src=decodedVideos[0]) + // Dash + source(src=decodedVideos[1]) + // Fallback + source(src=decodedVideos[2]) else if isPostLink(p) a(href=p.url) - | ↗ - - details(id=`${p.id}` open=(query.view == "card" && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)))) - summary.expand-post expand media - div.image-viewer - if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)) - div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) - h2 - != p.over_18 ? 'nsfw' : 'spoiler' - if isPostGallery(p) - div.gallery - each item in postGalleryItems(p) - div.gallery-item - a(href=`/media/${item.url}`) - img(src=item.url loading="lazy") - div.gallery-item-idx - | #{`${item.idx}/${item.total}`} - else if isPostImage(p) - a(href=`/media/${p.url}`) - img(src=p.url loading="lazy") - else if isPostVideo(p) - - var decodedVideos = decodePostVideoUrls(p) - video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy") - // HLS - source(src=decodedVideos[0]) - // Dash - source(src=decodedVideos[1]) - // Fallback - source(src=decodedVideos[2]) - if (query.view == "compact") - button(onclick=`toggleDetails('${p.id}')`) - | close + | #{p.domain} ↗ + if (query.view == "compact") + button(onclick=`toggleDetails('${p.id}')`) + | close diff --git a/src/public/styles.css b/src/public/styles.css index 8bfdc72..bc0e1ec 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -302,19 +302,14 @@ summary::before { object-fit: fill; } -.image-viewer a { - font-size: 1.5rem; - padding: unset; - padding-left: 1rem; -} - .image-viewer a:has(img) { font-size: 0rem; padding: unset; } -.media-preview a { - font-size: 2rem; +.media-preview a, +.image-viewer a { + font-size: 1.5rem; text-decoration: none; padding: 1rem; } @@ -479,7 +474,7 @@ form { margin-top: 10px; } -.post-container { +.post-info { display: flex; flex-direction: row; align-items: center; @@ -618,6 +613,13 @@ details:not([open]) summary::before { padding-right: 10px; } +details.compact { + /* + display: flex; + flex-basis: 100%; + */ +} + .footer { padding-bottom: 40px; } From 5f75dc5f53255542dd00ef554bcb72357365d197 Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Sat, 11 Jan 2025 16:47:27 +0000 Subject: [PATCH 3/9] Minor image/gallery/link tweaks post-consolidation. * Brings `comments` in line with `card` / `compact` gallery and `.image-viewer` styles. * Tweaks link positioning for `link` posts. * Force `video` scrubber previews to autoplay inline, mainly for mobile screens. * Adds `.image-viewer.main-content` style to handle media sizing and tweaks on `comments` view separate from `card` / `compact` views. * Switch to use `vmin` instead of `px` for `--border-radius-*` vars to properly scale across views and viewport sizes. --- src/mixins/post.pug | 2 +- src/public/styles.css | 27 +++++++++++++++------------ src/views/comments.pug | 35 ++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/mixins/post.pug b/src/mixins/post.pug index 4ec7614..472cab2 100644 --- a/src/mixins/post.pug +++ b/src/mixins/post.pug @@ -47,7 +47,7 @@ mixin post(p, currentUrl) img(src=url onclick=onclick) else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - video(autoplay="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") + video(autoplay="" playsinline="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") // Scrubber source(src=decodedVideos[3]) else if isPostLink(p) diff --git a/src/public/styles.css b/src/public/styles.css index bc0e1ec..deaf63d 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -11,9 +11,9 @@ --link-visited-color: #999; --accent: var(--link-color); --error-text-color: red; - --border-radius-card: 8px; - --border-radius-media: 6px; - --border-radius-preview: 4px; + --border-radius-card: 4vmin; + --border-radius-media: 3vmin; + --border-radius-preview: 2vmin; font-family: Inter, sans-serif; font-feature-settings: 'ss01' 1, 'kern' 1, 'liga' 1, 'cv05' 1, 'dlig' 1, 'ss01' 1, 'ss07' 1, 'ss08' 1; @@ -302,6 +302,15 @@ summary::before { object-fit: fill; } +.image-viewer.main-content img, +.image-viewer.main-content video { + max-height: 70vh; +} + +.image-viewer.main-content a { + margin: unset; +} + .image-viewer a:has(img) { font-size: 0rem; padding: unset; @@ -311,7 +320,8 @@ summary::before { .image-viewer a { font-size: 1.5rem; text-decoration: none; - padding: 1rem; + padding: unset; + margin: 1rem; } .media-maximized { @@ -415,7 +425,7 @@ form { } .image-viewer a { font-size: 1rem; - margin: 0.5rem; + margin: 1rem; padding: initial; } .self-text.card { @@ -613,13 +623,6 @@ details:not([open]) summary::before { padding-right: 10px; } -details.compact { - /* - display: flex; - flex-basis: 100%; - */ -} - .footer { padding-bottom: 40px; } diff --git a/src/views/comments.pug b/src/views/comments.pug index 1caf65a..1265609 100644 --- a/src/views/comments.pug +++ b/src/views/comments.pug @@ -47,23 +47,24 @@ html h2.post-title != post.title - if isPostGallery(post) - div.gallery - each item in postGalleryItems(post) - div.gallery-item - div.gallery-item-idx - | #{`${item.idx}/${item.total}`} - a(href=`/media/${item.url}`) - img(src=item.url loading="lazy") - else if isPostImage(post) - a(href=`/media/${post.url}`) - img(src=post.url).post-media - else if isPostVideo(post) - - var url = post.secure_media.reddit_video.dash_url - video(controls data-dashjs-player src=`${url}`).post-media - else if isPostLink(post) - a(href=post.url) - | #{post.url} + div.image-viewer.main-content + if isPostGallery(post) + div.gallery + each item in postGalleryItems(post) + div.gallery-item + a(href=`/media/${item.url}`) + img(src=item.url loading="lazy") + div.gallery-item-idx + | #{`${item.idx}/${item.total}`} + else if isPostImage(post) + a(href=`/media/${post.url}`) + img(src=post.url).post-media + else if isPostVideo(post) + - var url = post.secure_media.reddit_video.dash_url + video(controls data-dashjs-player src=`${url}`).post-media + else if isPostLink(post) + a(href=post.url) + | #{post.domain} ↗ if post.selftext_html div.self-text From 1c02073d7be1d04d83825193d6c9c8458e70b8a3 Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Sat, 11 Jan 2025 20:53:28 +0000 Subject: [PATCH 4/9] Fix `border-radius` on larger screen sizes --- src/public/styles.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/public/styles.css b/src/public/styles.css index deaf63d..22ee99f 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -362,6 +362,11 @@ form { } @media (min-width: 768px) { + :root { + --border-radius-card: 2vmin; + --border-radius-media: 1vmin; + --border-radius-preview: 0.5vmin; + } .post, .comments-container, .hero, .header, .footer { flex: 1 1 90%; width: 90%; @@ -404,6 +409,10 @@ form { } @media (min-width: 1080px) { + :root { + --border-radius-card: 1vmin; + --border-radius-media: 1vmin; + } .post, .comments-container, .hero, .header, .footer { flex: 1 1 60%; width: 60%; @@ -450,6 +459,10 @@ form { } @media (min-width: 2560px) { + :root { + --border-radius-card: 0.75vmin; + --border-radius-media: 0.75vmin; + } .post, .comments-container, .hero, .header, .footer { flex: 1 1 40%; width: 40%; From 7bc78e3a51e862a86ba79465caada9093154fec8 Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Sat, 11 Jan 2025 21:40:24 +0000 Subject: [PATCH 5/9] Tweak CSS for `spoiler` / `nsfw` in card view and `max-height` in `card` view. --- src/public/styles.css | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/public/styles.css b/src/public/styles.css index 22ee99f..71ad878 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -199,8 +199,12 @@ nav { .image-viewer { position: relative; - padding: 0.3rem; - padding-bottom: 0.3rem; + margin: 0.9rem; + margin-bottom: 0.3rem; +} + +.image-viewer:not(:has(details)) { + margin: unset; } .image-viewer > img { @@ -281,15 +285,11 @@ summary::before { border-radius: var(--border-radius-preview); } -.image-viewer { - padding: unset; -} - .image-viewer img, .image-viewer video { border-radius: var(--border-radius-media); - max-height: 40vh; + max-height: 50vh; max-width: 95%; display: block; @@ -380,7 +380,7 @@ form { .image-viewer img, .image-viewer video { - max-height: 50vh; + max-height: 45vh; } .image-viewer a { font-size: 1rem; @@ -426,7 +426,7 @@ form { .image-viewer img, .image-viewer video { - max-height: 30vh; + max-height: 35vh; } .media-preview a { font-size: 2rem; @@ -470,7 +470,7 @@ form { .image-viewer img, .image-viewer video { - max-height: 20vh; + max-height: 30vh; } .sort-opts, .view-opts { @@ -667,11 +667,6 @@ a { text-align: center; } -.gallery img { - width: auto; - max-height: 500px; -} - .post-title { margin-top: 5px; } From 24be4c6689e2d184147e53443ea79a7650835a9e Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Tue, 14 Jan 2025 17:10:26 +0000 Subject: [PATCH 6/9] More CSS cleanup and (hopefully) final tweaks. * Further adjusted `--border-radius-*` vars to reduce egregious curvature. * Adjusted `post-text` padding and max width in `card` view to reduce text overflow on smaller screens. * Left `details` collapsed in `card` view when there is no media to preview. * Corrected `.image-viewer` margins when `details` is collapsed in `card` view. * Added `isPostMedia` function to `postUtils` to consolidate some `if` statements. * Fixed links not showing in `card` view if there was no other media. --- src/mixins/post.pug | 6 +++--- src/mixins/postUtils.pug | 4 ++++ src/public/styles.css | 41 ++++++++++++++++++++-------------------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/mixins/post.pug b/src/mixins/post.pug index 472cab2..676b349 100644 --- a/src/mixins/post.pug +++ b/src/mixins/post.pug @@ -28,7 +28,7 @@ mixin post(p, currentUrl) a(href=`/r/${p.subreddit}?sort=${sortQuery}&view=${viewQuery}`) r/#{p.subreddit} |  ·  a(href=`/comments/${p.id}?from=${from}&sort=${sortQuery}&view=${viewQuery}`) #{fmtnum (p.num_comments)} ↩ - if (query.view == "card" && !isPostGallery(p) && !isPostImage(p) && !isPostVideo(p) && p.selftext_html) + if (query.view == "card" && !isPostMedia(p) && p.selftext_html) div.self-text-overflow.card if p.spoiler || p.over_18 div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) @@ -54,10 +54,10 @@ mixin post(p, currentUrl) a(href=p.url) | ↗ - details(id=`${p.id}` open=(query.view == "card") class=`${query.view}`) + details(id=`${p.id}` open=(query.view == "card" && (isPostMedia(p) || isPostLink(p))) class=`${query.view}`) summary.expand-post expand media div.image-viewer - if query.view == "card" && (p.spoiler || p.over_18) && (isPostGallery(p) || isPostImage(p) || isPostVideo(p)) + if query.view == "card" && (p.spoiler || p.over_18) && isPostMedia(p) div.spoiler(id=`spoiler_${p.id}`, onclick=`javascript:document.getElementById('spoiler_${p.id}').style.display = 'none';`) h2 != p.over_18 ? 'nsfw' : 'spoiler' diff --git a/src/mixins/postUtils.pug b/src/mixins/postUtils.pug index b96ff1e..7eef9b2 100644 --- a/src/mixins/postUtils.pug +++ b/src/mixins/postUtils.pug @@ -1,3 +1,7 @@ +- + function isPostMedia(p) { + return isPostImage(p) || isPostGallery(p) || isPostVideo(p); + } - function isPostGallery(p) { return (p.is_gallery && p.is_gallery == true); diff --git a/src/public/styles.css b/src/public/styles.css index 71ad878..580dbf9 100644 --- a/src/public/styles.css +++ b/src/public/styles.css @@ -11,9 +11,9 @@ --link-visited-color: #999; --accent: var(--link-color); --error-text-color: red; - --border-radius-card: 4vmin; - --border-radius-media: 3vmin; - --border-radius-preview: 2vmin; + --border-radius-card: 2vmin; + --border-radius-media: 1.5vmin; + --border-radius-preview: 1vmin; font-family: Inter, sans-serif; font-feature-settings: 'ss01' 1, 'kern' 1, 'liga' 1, 'cv05' 1, 'dlig' 1, 'ss01' 1, 'ss07' 1, 'ss08' 1; @@ -174,7 +174,10 @@ nav { .post-text.card { padding: 0.9rem; - padding-top: 0.3rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + overflow-wrap: break-word; + max-width: 95%; } .self-text-overflow.card { @@ -185,6 +188,7 @@ nav { overflow: hidden; overflow-wrap: break-word; display: block; + max-width: 98%; } .self-text.card { @@ -200,11 +204,6 @@ nav { .image-viewer { position: relative; margin: 0.9rem; - margin-bottom: 0.3rem; -} - -.image-viewer:not(:has(details)) { - margin: unset; } .image-viewer > img { @@ -314,6 +313,7 @@ summary::before { .image-viewer a:has(img) { font-size: 0rem; padding: unset; + margin: unset; } .media-preview a, @@ -363,7 +363,7 @@ form { @media (min-width: 768px) { :root { - --border-radius-card: 2vmin; + --border-radius-card: 1vmin; --border-radius-media: 1vmin; --border-radius-preview: 0.5vmin; } @@ -387,9 +387,11 @@ form { margin: 0.7rem; padding: initial; } - .self-text.card { - -webkit-line-clamp: 4; - line-clamp: 4; + .post-text.card { + max-width: 100%; + } + .self-text-overflow.card { + max-width: 100%; } .post-author { display: inline @@ -410,8 +412,9 @@ form { @media (min-width: 1080px) { :root { - --border-radius-card: 1vmin; - --border-radius-media: 1vmin; + --border-radius-card: 0.5vmin; + --border-radius-media: 0.5vmin; + --border-radius-preview: 0.3vmin; } .post, .comments-container, .hero, .header, .footer { flex: 1 1 60%; @@ -438,8 +441,8 @@ form { padding: initial; } .self-text.card { - -webkit-line-clamp: 6; - line-clamp: 6; + -webkit-line-clamp: 4; + line-clamp: 4; } .post-author { display: inline @@ -459,10 +462,6 @@ form { } @media (min-width: 2560px) { - :root { - --border-radius-card: 0.75vmin; - --border-radius-media: 0.75vmin; - } .post, .comments-container, .hero, .header, .footer { flex: 1 1 40%; width: 40%; From 24af351b986717ec74a0e63c8844a574fae82b5b Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Thu, 16 Jan 2025 00:51:22 +0000 Subject: [PATCH 7/9] Revert to `src` attribute in `video` tags for DashJS. Should work in iOS 18.2+, but does not work in iOS 17 and below. --- src/mixins/post.pug | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/mixins/post.pug b/src/mixins/post.pug index 676b349..8210ad8 100644 --- a/src/mixins/post.pug +++ b/src/mixins/post.pug @@ -47,7 +47,7 @@ mixin post(p, currentUrl) img(src=url onclick=onclick) else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - video(autoplay="" playsinline="" muted="" data-dashjs-player="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") + video(data-dashjs-player="" playsinline="" autoplay="" muted="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") // Scrubber source(src=decodedVideos[3]) else if isPostLink(p) @@ -74,13 +74,7 @@ mixin post(p, currentUrl) img(src=p.url loading="lazy") else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - video(controls="" muted="" data-dashjs-player="" preload="metadata" playsinline="" poster=decodedVideos[4] objectfit="contain" loading="lazy") - // HLS - source(src=decodedVideos[0]) - // Dash - source(src=decodedVideos[1]) - // Fallback - source(src=decodedVideos[2]) + video(data-dashjs-player="" playsinline="" controls="" muted="" preload="metadata" poster=decodedVideos[4] src=decodedVideos[1]) else if isPostLink(p) a(href=p.url) | #{p.domain} ↗ From 6cd1bd918531de368605447c3f9a1495cd8dbe1c Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Sun, 19 Jan 2025 23:48:56 +0000 Subject: [PATCH 8/9] Tweak `src`/`poster` order on videos for more consistent behavior. --- src/mixins/post.pug | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mixins/post.pug b/src/mixins/post.pug index 8210ad8..9021f93 100644 --- a/src/mixins/post.pug +++ b/src/mixins/post.pug @@ -47,9 +47,7 @@ mixin post(p, currentUrl) img(src=url onclick=onclick) else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - video(data-dashjs-player="" playsinline="" autoplay="" muted="" onclick=`toggleDetails('${p.id}')` poster=decodedVideos[4] width="100px" height="100px") - // Scrubber - source(src=decodedVideos[3]) + video(data-dashjs-player="" playsinline="" autoplay="" muted="" onclick=`toggleDetails('${p.id}')` src=decodedVideos[3] poster=decodedVideos[4] width="100px" height="100px") else if isPostLink(p) a(href=p.url) | ↗ @@ -74,7 +72,7 @@ mixin post(p, currentUrl) img(src=p.url loading="lazy") else if isPostVideo(p) - var decodedVideos = decodePostVideoUrls(p) - video(data-dashjs-player="" playsinline="" controls="" muted="" preload="metadata" poster=decodedVideos[4] src=decodedVideos[1]) + video(data-dashjs-player="" playsinline="" controls="" muted="" preload="metadata" src=decodedVideos[1] poster=decodedVideos[4]) else if isPostLink(p) a(href=p.url) | #{p.domain} ↗ From 7e73b06afc434fe6a62166ead138345010c22b8e Mon Sep 17 00:00:00 2001 From: PortableProgrammer Date: Sat, 11 Jan 2025 20:11:28 +0000 Subject: [PATCH 9/9] Media View: scroll to pointer when zooming. --- src/views/media.pug | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/views/media.pug b/src/views/media.pug index e945704..3a59542 100644 --- a/src/views/media.pug +++ b/src/views/media.pug @@ -3,13 +3,18 @@ doctype html html +head("home") script(type='text/javascript'). - function toggleZoom() { + function toggleZoom(event) { + const percentX = event.offsetX / event.target.width; + const percentY = event.offsetY / event.target.height; Array.from(document.getElementsByClassName('media-maximized')).forEach(element => element.classList.toggle('zoom')); + const moveClientX = (event.target.width * percentX) + event.target.offsetLeft - (event.view.visualViewport.width / 2) + const moveClientY = (event.target.height * percentY) + event.target.offsetTop - (event.view.visualViewport.height / 2); + event.target.parentElement.scrollTo(moveClientX, moveClientY); } body.media-maximized div.media-maximized.container if kind == 'img' - img(src=url onclick=`toggleZoom()`).media-maximized + img(src=url onclick=`toggleZoom(event)`).media-maximized else video(src=url controls).media-maximized