| 1 | <div class="tl-wrap">
|
|---|
| 2 | <%- include('../partials/fedi-tabs', { active: 'feed' }) %>
|
|---|
| 3 | <div class="tl-titlebar">
|
|---|
| 4 | <h1 class="tl-title"><%= t('tl.title') %></h1>
|
|---|
| 5 | <button type="button" id="tl-paste-btn" class="tl-paste-btn" title="<%= t('fedi.remote_interact') %>" aria-label="<%= t('fedi.remote_interact') %>">
|
|---|
| 6 | <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|---|
| 7 | </button>
|
|---|
| 8 | </div>
|
|---|
| 9 | <p class="tl-lead"><%= t('tl.lead') %></p>
|
|---|
| 10 |
|
|---|
| 11 | <div class="tl-paste" id="tl-paste">
|
|---|
| 12 | <div class="tl-paste-card" role="dialog" aria-label="<%= t('fedi.remote_interact') %>">
|
|---|
| 13 | <button type="button" class="tl-paste-x" aria-label="<%= t('fedi.cancel') %>">×</button>
|
|---|
| 14 | <h3 class="tl-paste-title"><%= t('fedi.remote_interact') %></h3>
|
|---|
| 15 | <input type="url" class="tl-paste-input" inputmode="url" autocomplete="off" autocapitalize="none" spellcheck="false" placeholder="<%= t('tl.paste_ph') %>">
|
|---|
| 16 | <div class="tl-paste-actions">
|
|---|
| 17 | <button type="button" class="tl-paste-cancel"><%= t('fedi.cancel') %></button>
|
|---|
| 18 | <button type="button" class="tl-paste-go"><%= t('tl.paste_go') %></button>
|
|---|
| 19 | </div>
|
|---|
| 20 | </div>
|
|---|
| 21 | </div>
|
|---|
| 22 | <style>
|
|---|
| 23 | .tl-titlebar { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
|
|---|
| 24 | .tl-boost-by { display: flex; align-items: center; gap: .35rem; font-size: .8rem; color: var(--ink-soft); margin: 0 0 .45rem .2rem; }
|
|---|
| 25 | .tl-boost-by svg { color: #16a34a; flex: 0 0 auto; }
|
|---|
| 26 | .tl-boost-by strong { color: var(--ink); font-weight: 600; }
|
|---|
| 27 | .tl-paste-btn { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px;
|
|---|
| 28 | border: 1px solid var(--rule); border-radius: 999px; background: var(--paper-2); color: var(--ink-soft); cursor: pointer; transition: color .15s, border-color .15s, background .15s; }
|
|---|
| 29 | .tl-paste-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|---|
| 30 | .tl-paste { position: fixed; inset: 0; z-index: 2147483601; display: none; align-items: center; justify-content: center; padding: 1rem; background: rgba(0,0,0,.55); }
|
|---|
| 31 | .tl-paste.is-open { display: flex; }
|
|---|
| 32 | .tl-paste-card { position: relative; width: 100%; max-width: 460px; background: var(--paper); color: var(--ink); border: 1px solid var(--rule);
|
|---|
| 33 | border-radius: 16px; padding: 1.4rem 1.4rem 1.2rem; box-shadow: 0 18px 50px rgba(0,0,0,.4); }
|
|---|
| 34 | .tl-paste-x { position: absolute; top: .5rem; right: .6rem; width: 2rem; height: 2rem; border: 0; border-radius: 999px; background: transparent; color: var(--ink-soft); font-size: 1.4rem; line-height: 1; cursor: pointer; }
|
|---|
| 35 | .tl-paste-title { margin: 0 0 .8rem; font-size: 1.1rem; }
|
|---|
| 36 | .tl-paste-input { width: 100%; box-sizing: border-box; padding: .6rem .8rem; border: 1.5px solid var(--rule); border-radius: 10px; background: var(--paper-2); color: var(--ink); font: inherit; font-size: .95rem; }
|
|---|
| 37 | .tl-paste-input:focus { outline: none; border-color: var(--accent); }
|
|---|
| 38 | .tl-paste-actions { display: flex; justify-content: flex-end; gap: .6rem; margin-top: 1rem; }
|
|---|
| 39 | .tl-paste-actions button { padding: .5rem 1.1rem; border-radius: 999px; font: inherit; font-weight: 600; font-size: .88rem; cursor: pointer; border: 0; }
|
|---|
| 40 | .tl-paste-cancel { background: transparent; border: 1.5px solid var(--rule); color: var(--ink); }
|
|---|
| 41 | .tl-paste-go { background: var(--accent); color: var(--paper); }
|
|---|
| 42 | </style>
|
|---|
| 43 | <script>
|
|---|
| 44 | (function () {
|
|---|
| 45 | if (window.__tlPasteWired) return; window.__tlPasteWired = true;
|
|---|
| 46 | function modal() { return document.getElementById('tl-paste'); }
|
|---|
| 47 | function openM() {
|
|---|
| 48 | var m = modal(); if (!m) return;
|
|---|
| 49 | var inp = m.querySelector('.tl-paste-input');
|
|---|
| 50 | m.classList.add('is-open');
|
|---|
| 51 | if (inp) {
|
|---|
| 52 | inp.value = ''; inp.focus();
|
|---|
| 53 | if (navigator.clipboard && navigator.clipboard.readText) {
|
|---|
| 54 | navigator.clipboard.readText().then(function (t) {
|
|---|
| 55 | t = (t || '').trim();
|
|---|
| 56 | if (/^https?:\/\//i.test(t) && !inp.value) { inp.value = t; inp.select(); }
|
|---|
| 57 | }).catch(function () {});
|
|---|
| 58 | }
|
|---|
| 59 | }
|
|---|
| 60 | }
|
|---|
| 61 | function closeM() { var m = modal(); if (m) m.classList.remove('is-open'); }
|
|---|
| 62 | function go() {
|
|---|
| 63 | var m = modal(); if (!m) return;
|
|---|
| 64 | var inp = m.querySelector('.tl-paste-input'), v = (inp ? inp.value : '').trim();
|
|---|
| 65 | if (!/^https?:\/\//i.test(v)) { if (inp) inp.focus(); return; }
|
|---|
| 66 | location.href = '/authorize_interaction?uri=' + encodeURIComponent(v);
|
|---|
| 67 | }
|
|---|
| 68 | document.addEventListener('click', function (e) {
|
|---|
| 69 | if (e.target.closest && e.target.closest('#tl-paste-btn')) { e.preventDefault(); openM(); return; }
|
|---|
| 70 | if (e.target.closest && e.target.closest('.tl-paste-go')) { e.preventDefault(); go(); return; }
|
|---|
| 71 | if (e.target.closest && (e.target.closest('.tl-paste-cancel') || e.target.closest('.tl-paste-x'))) { e.preventDefault(); closeM(); return; }
|
|---|
| 72 | var op = document.querySelector('.tl-paste.is-open'); if (op && e.target === op) closeM();
|
|---|
| 73 | });
|
|---|
| 74 | document.addEventListener('keydown', function (e) {
|
|---|
| 75 | var m = document.querySelector('.tl-paste.is-open'); if (!m) return;
|
|---|
| 76 | if (e.key === 'Escape') closeM();
|
|---|
| 77 | else if (e.key === 'Enter' && e.target.closest && e.target.closest('.tl-paste')) { e.preventDefault(); go(); }
|
|---|
| 78 | });
|
|---|
| 79 | })();
|
|---|
| 80 | </script>
|
|---|
| 81 | <% if (typeof success !== 'undefined' && success) { %><div class="alert alert-success"><%= success %></div><% } %>
|
|---|
| 82 | <% if (typeof error !== 'undefined' && error) { %><div class="alert alert-error"><%= error %></div><% } %>
|
|---|
| 83 |
|
|---|
| 84 | <% if (!timeline || !timeline.length) { %>
|
|---|
| 85 | <div class="tl-empty">
|
|---|
| 86 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
|
|---|
| 87 | <p><%= t('tl.empty') %></p>
|
|---|
| 88 | </div>
|
|---|
| 89 | <% } else { %>
|
|---|
| 90 | <ol class="tl-feed">
|
|---|
| 91 | <% timeline.forEach(function(p){ %>
|
|---|
| 92 | <li class="tl-item">
|
|---|
| 93 | <% if (p.reblog_name) { %><div class="tl-boost-by"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg> <strong><%= p.reblog_name %></strong> <%= t('tl.boosted') %></div><% } %>
|
|---|
| 94 | <div class="tl-head">
|
|---|
| 95 | <span class="tl-avatar"><% if (p.author_icon) { %><img src="<%= avatar(p.author_icon, 128) %>" alt="" loading="lazy"><% } else { %><%= (p.author_name || '?').charAt(0).toUpperCase() %><% } %></span>
|
|---|
| 96 | <span class="tl-id">
|
|---|
| 97 | <a class="tl-author" href="<%= p.author_url || p.author_uri %>" target="_blank" rel="nofollow noopener"><%= p.author_name %></a>
|
|---|
| 98 | <span class="tl-handle"><%= p.author_handle %></span>
|
|---|
| 99 | </span>
|
|---|
| 100 | <% if (p.published || p.created_at) { %><time class="tl-time"><%= formatDateTime(p.published || p.created_at) %></time><% } %>
|
|---|
| 101 | </div>
|
|---|
| 102 |
|
|---|
| 103 | <%
|
|---|
| 104 | var media = []; try { media = JSON.parse(p.media_json || '[]'); } catch (e) { media = []; }
|
|---|
| 105 | var imgs = media.filter(function(m){ return m && m.url && (!m.type || /^image\//.test(m.type)); });
|
|---|
| 106 | var vids = media.filter(function(m){ return m && m.url && m.type && /^video\//.test(m.type); });
|
|---|
| 107 | var auds = media.filter(function(m){ return m && m.url && m.type && /^audio\//.test(m.type); });
|
|---|
| 108 | var _nsfwVisual = !!p.nsfw && (imgs.length || vids.length || auds.length);
|
|---|
| 109 | var _nsfwText = !!p.nsfw && !(imgs.length || vids.length || auds.length);
|
|---|
| 110 | %>
|
|---|
| 111 | <% if (_nsfwText) { %>
|
|---|
| 112 | <div class="tl-content nsfw-media"><span class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= p.cw || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></span><%- p.content %></div>
|
|---|
| 113 | <% } else { %>
|
|---|
| 114 | <div class="tl-content"><%- p.content %></div>
|
|---|
| 115 | <% } %>
|
|---|
| 116 | <% if (_nsfwVisual) { %><div class="nsfw-media"><% } %>
|
|---|
| 117 | <% if (imgs.length) { %>
|
|---|
| 118 | <div class="tl-media">
|
|---|
| 119 | <% imgs.forEach(function(m){ %>
|
|---|
| 120 | <a class="tl-media-img" href="<%= m.url %>" target="_blank" rel="noopener"><img src="<%= m.url %>" alt="" loading="lazy" decoding="async"></a>
|
|---|
| 121 | <% }); %>
|
|---|
| 122 | </div>
|
|---|
| 123 | <% } %>
|
|---|
| 124 | <% vids.forEach(function(m){ %><video class="tl-media-video" src="<%= m.url %>" controls preload="metadata" playsinline></video><% }); %>
|
|---|
| 125 | <% auds.forEach(function(m){ %><audio class="tl-media-audio" src="<%= m.url %>" controls preload="none"></audio><% }); %>
|
|---|
| 126 | <% if (_nsfwVisual) { %><div class="nsfw-veil"><span class="nsfw-veil-label">🔞 <%= p.cw || t('post.nsfw_warning') %></span><span class="nsfw-veil-btn"><%= t('post.nsfw_show') %></span></div></div><% } %>
|
|---|
| 127 |
|
|---|
| 128 | <% if (p.embedHtml) { %><div class="tl-embed"><%- p.embedHtml %></div><% } %>
|
|---|
| 129 | <% if (p.embedUrl) { %><a class="tl-embed-open" href="<%= p.embedUrl %>" target="_blank" rel="noopener">▶ <%= t('tl.open_player') %></a><% } %>
|
|---|
| 130 |
|
|---|
| 131 | <% if (p.url) { %><a class="tl-orig" href="<%= p.url %>" target="_blank" rel="nofollow noopener"><%= t('tl.view_original') %></a><% } %>
|
|---|
| 132 |
|
|---|
| 133 | <div class="tl-actions">
|
|---|
| 134 | <form method="post" action="/news/like" class="tl-act-form tl-react-form"><input type="hidden" name="note" value="<%= p.id %>"><input type="hidden" name="author" value="<%= p.author_uri %>"><button type="submit" class="tl-act tl-act-like<%= p.liked ? ' is-on' : '' %>" title="<%= p.liked ? t('fedi.unlike_short') : t('fedi.likes') %>" aria-label="<%= p.liked ? t('fedi.unlike_short') : t('fedi.likes') %>"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.9 5.88 6.49.95-4.7 4.58 1.11 6.46L12 17.96l-5.8 3.06 1.1-6.46-4.69-4.58 6.49-.95z"/></svg></button></form>
|
|---|
| 135 | <form method="post" action="/news/boost" class="tl-act-form tl-react-form"><input type="hidden" name="note" value="<%= p.id %>"><input type="hidden" name="author" value="<%= p.author_uri %>"><button type="submit" class="tl-act tl-act-boost<%= p.boosted ? ' is-on' : '' %>" title="<%= p.boosted ? t('tl.unboost') : t('fedi.boosts') %>" aria-label="<%= p.boosted ? t('tl.unboost') : t('fedi.boosts') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg></button></form>
|
|---|
| 136 | <button type="button" class="tl-act tl-act-reply fedi-remote-reply-btn" data-fedi-uri="<%= p.id %>" data-fedi-ph="<%= t('fedi.remote_ph') %>" title="<%= t('fedi.remote_reply') %>" aria-label="<%= t('fedi.remote_reply') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg></button>
|
|---|
| 137 | <form method="post" action="/blocking/add" class="tl-act-form" data-confirm="<%= t('tl.block') %>?"><input type="hidden" name="target" value="<%= p.author_uri %>"><button type="submit" class="tl-act tl-act-block" title="<%= t('tl.block') %>" aria-label="<%= t('tl.block') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="4.9" y1="4.9" x2="19.1" y2="19.1"/></svg></button></form>
|
|---|
| 138 | </div>
|
|---|
| 139 | </li>
|
|---|
| 140 | <% }); %>
|
|---|
| 141 | </ol>
|
|---|
| 142 | <% } %>
|
|---|
| 143 | </div>
|
|---|
| 144 |
|
|---|
| 145 | <style>
|
|---|
| 146 | .tl-wrap { max-width: 640px; margin: 1rem auto 3rem; padding: 0 1rem; }
|
|---|
| 147 | .tl-title { margin: 0 0 .15rem; font-size: clamp(1.5rem, 4vw, 1.8rem); }
|
|---|
| 148 | .tl-lead { color: var(--ink-soft, #888); margin: 0 0 1.5rem; }
|
|---|
| 149 | .tl-empty { text-align: center; color: var(--ink-soft, #888); margin: 3rem auto; }
|
|---|
| 150 | .tl-empty svg { width: 44px; height: 44px; color: color-mix(in srgb, var(--accent, #888) 60%, transparent); margin: 0 auto .75rem; display: block; }
|
|---|
| 151 |
|
|---|
| 152 | .tl-feed { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 1rem; }
|
|---|
| 153 | .tl-item { padding: 1.1rem 1.15rem; border-radius: 16px;
|
|---|
| 154 | border: 1px solid color-mix(in srgb, var(--ink, #000) 8%, transparent);
|
|---|
| 155 | background: color-mix(in srgb, var(--ink, #000) 2.5%, transparent);
|
|---|
| 156 | transition: border-color .15s ease, box-shadow .15s ease; }
|
|---|
| 157 | .tl-item:hover { border-color: color-mix(in srgb, var(--accent, #888) 32%, transparent);
|
|---|
| 158 | box-shadow: 0 2px 14px color-mix(in srgb, var(--ink, #000) 6%, transparent); }
|
|---|
| 159 |
|
|---|
| 160 | .tl-head { display: flex; align-items: center; gap: .65rem; margin: 0 0 .65rem; }
|
|---|
| 161 | .tl-avatar { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%; overflow: hidden;
|
|---|
| 162 | display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: 1.1rem;
|
|---|
| 163 | background: color-mix(in srgb, var(--accent, #888) 22%, transparent); color: var(--accent, #555);
|
|---|
| 164 | box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent, #888) 18%, transparent); }
|
|---|
| 165 | .tl-avatar img { width: 100%; height: 100%; object-fit: cover; background: #fff; }
|
|---|
| 166 | .tl-id { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
|
|---|
| 167 | .tl-author { font-weight: 700; color: var(--ink, inherit); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|---|
| 168 | .tl-author:hover { text-decoration: underline; }
|
|---|
| 169 | .tl-handle { color: var(--ink-soft, #888); font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|---|
| 170 | .tl-time { color: var(--ink-soft, #999); font-size: .78rem; flex: 0 0 auto; align-self: flex-start; white-space: nowrap; }
|
|---|
| 171 |
|
|---|
| 172 | .tl-content { line-height: 1.55; overflow-wrap: anywhere; }
|
|---|
| 173 | .tl-content p { margin: .4rem 0; } .tl-content p:first-child { margin-top: 0; } .tl-content p:last-child { margin-bottom: 0; }
|
|---|
| 174 | .tl-content a { color: var(--accent, #06c); }
|
|---|
| 175 | .tl-content img { max-width: 100%; height: auto; border-radius: 10px; background: #fff; }
|
|---|
| 176 | /* Long posts collapse to a max height with a fade + "read more" (added by JS only when it overflows). */
|
|---|
| 177 | .tl-content.tl-clamp { max-height: 20em; overflow: hidden;
|
|---|
| 178 | -webkit-mask-image: linear-gradient(180deg, #000 72%, transparent);
|
|---|
| 179 | mask-image: linear-gradient(180deg, #000 72%, transparent); }
|
|---|
| 180 | .tl-readmore { display: inline-block; margin: .15rem 0 .1rem; padding: .15rem 0;
|
|---|
| 181 | background: none; border: 0; color: var(--accent, #06c); font: inherit; font-weight: 600;
|
|---|
| 182 | font-size: .85rem; cursor: pointer; }
|
|---|
| 183 | .tl-readmore:hover { text-decoration: underline; }
|
|---|
| 184 |
|
|---|
| 185 | /* Always show the FULL image at its natural ratio — never cropped, no letterbox. */
|
|---|
| 186 | .tl-media { display: flex; flex-direction: column; gap: .4rem; margin: .75rem 0 0; }
|
|---|
| 187 | .tl-media-img { display: block; border-radius: 12px; overflow: hidden;
|
|---|
| 188 | background-image: linear-gradient(135deg,
|
|---|
| 189 | color-mix(in srgb, var(--accent, #888) 22%, var(--paper-2, var(--paper))) 0%,
|
|---|
| 190 | color-mix(in srgb, var(--accent, #888) 6%, var(--paper-2, var(--paper))) 100%); }
|
|---|
| 191 | .tl-media-img img { width: 100%; height: auto; display: block; background: #fff; transition: opacity .4s ease; }
|
|---|
| 192 | .tl-media-img img.is-loading { opacity: 0; }
|
|---|
| 193 | /* Reserve placeholder space while the (natural-aspect) feed image loads. */
|
|---|
| 194 | .tl-media-img:has(img.is-loading) { min-height: 220px; }
|
|---|
| 195 |
|
|---|
| 196 | .tl-media-video, .tl-media-audio { width: 100%; margin: .75rem 0 0; border-radius: 12px; display: block; }
|
|---|
| 197 | .tl-media-video { max-height: 480px; background: #000; }
|
|---|
| 198 |
|
|---|
| 199 | .tl-embed { margin: .75rem 0 0; border-radius: 12px; overflow: hidden; background: var(--paper-2, #111); }
|
|---|
| 200 | .tl-embed-frame { width: 100%; border: 0; display: block; aspect-ratio: 16 / 9; }
|
|---|
| 201 | .tl-embed-spotify { aspect-ratio: auto; height: 152px; }
|
|---|
| 202 | .tl-embed-sc { aspect-ratio: auto; height: 166px; }
|
|---|
| 203 | .tl-embed-klonkt { aspect-ratio: auto; height: 180px; background: var(--paper, #fff); }
|
|---|
| 204 | .tl-embed-bandcamp { aspect-ratio: auto; height: 470px; }
|
|---|
| 205 | .tl-embed-apple { aspect-ratio: auto; height: 175px; }
|
|---|
| 206 | .tl-embed-open { display: inline-block; margin: .45rem .9rem 0 0; font-size: .82rem; font-weight: 600; color: var(--accent); text-decoration: none; }
|
|---|
| 207 | .tl-embed-open:hover { text-decoration: underline; }
|
|---|
| 208 |
|
|---|
| 209 | .tl-orig { display: inline-block; margin: .7rem 0 0; font-size: .82rem; color: var(--accent, #06c); text-decoration: none; }
|
|---|
| 210 | .tl-orig:hover { text-decoration: underline; }
|
|---|
| 211 |
|
|---|
| 212 | /* Uniform circular action buttons with coloured icons (no emoji). */
|
|---|
| 213 | .tl-actions { display: flex; gap: .5rem; margin-top: .9rem; align-items: center; }
|
|---|
| 214 | .tl-act-form { margin: 0; display: inline-flex; }
|
|---|
| 215 | .tl-act { width: 36px; height: 36px; flex: 0 0 36px; border-radius: 50%; padding: 0; box-sizing: border-box;
|
|---|
| 216 | display: inline-flex; align-items: center; justify-content: center;
|
|---|
| 217 | border: 1px solid color-mix(in srgb, var(--ink, #000) 12%, transparent);
|
|---|
| 218 | background: color-mix(in srgb, var(--ink, #000) 3%, transparent);
|
|---|
| 219 | color: var(--ink-soft, #888); cursor: pointer; -webkit-tap-highlight-color: transparent;
|
|---|
| 220 | transition: background .12s, border-color .12s, transform .1s; }
|
|---|
| 221 | .tl-act svg { width: 18px; height: 18px; display: block; }
|
|---|
| 222 | .tl-act:active { transform: scale(.9); }
|
|---|
| 223 | .tl-act-like svg { color: #e8b04b; }
|
|---|
| 224 | .tl-act-like:hover { background: color-mix(in srgb, #e8b04b 15%, transparent); border-color: color-mix(in srgb, #e8b04b 50%, transparent); }
|
|---|
| 225 | .tl-act-like.is-on { background: color-mix(in srgb, #e8b04b 18%, transparent); border-color: color-mix(in srgb, #e8b04b 60%, transparent); }
|
|---|
| 226 | .tl-act-boost svg { color: #2fa85a; }
|
|---|
| 227 | .tl-act-boost:hover { background: color-mix(in srgb, #2fa85a 15%, transparent); border-color: color-mix(in srgb, #2fa85a 50%, transparent); }
|
|---|
| 228 | .tl-act-boost.is-on { background: color-mix(in srgb, #2fa85a 18%, transparent); border-color: color-mix(in srgb, #2fa85a 60%, transparent); }
|
|---|
| 229 | .tl-act-reply svg { color: var(--accent, #06c); }
|
|---|
| 230 | .tl-act-reply:hover { background: color-mix(in srgb, var(--accent, #888) 14%, transparent); border-color: color-mix(in srgb, var(--accent, #888) 50%, transparent); }
|
|---|
| 231 | .tl-act-block svg { color: #e0524d; }
|
|---|
| 232 | .tl-act-block:hover { background: color-mix(in srgb, #d9534f 14%, transparent); border-color: color-mix(in srgb, #d9534f 50%, transparent); }
|
|---|
| 233 | </style>
|
|---|
| 234 |
|
|---|
| 235 | <script>
|
|---|
| 236 | (function(){
|
|---|
| 237 | if (window.__fediRemoteWired) return; window.__fediRemoteWired = true;
|
|---|
| 238 | var current = null, currentBtn = null;
|
|---|
| 239 | function close(){ if (current) { current.remove(); current = null; currentBtn = null; } }
|
|---|
| 240 | function go(raw, uri){ var d=(raw||'').trim().replace(/^@?[^@\s]*@/,'').replace(/^https?:\/\//i,'').replace(/\/.*$/,'').trim(); if(d){ try{ localStorage.setItem('pcmsFediServer', d); }catch(e){} location.href='https://'+d+'/authorize_interaction?uri='+encodeURIComponent(uri||''); } }
|
|---|
| 241 | function place(f, b){ var r=b.getBoundingClientRect(); f.style.top=(r.bottom+window.scrollY+6)+'px'; f.style.left=Math.max(8, Math.min(r.left+window.scrollX, window.scrollX+window.innerWidth-340))+'px'; }
|
|---|
| 242 | document.addEventListener('click', function(e){
|
|---|
| 243 | if (e.target.closest && e.target.closest('.fedi-remote-cancel')) { close(); return; }
|
|---|
| 244 | if (current && e.target.closest && e.target.closest('.fedi-remote-form')) return;
|
|---|
| 245 | var b = e.target.closest && e.target.closest('.fedi-remote-reply-btn');
|
|---|
| 246 | if (b) {
|
|---|
| 247 | e.preventDefault();
|
|---|
| 248 | if (currentBtn === b) { close(); return; }
|
|---|
| 249 | close();
|
|---|
| 250 | var f = document.createElement('form'); f.className='fedi-remote-form'; f.dataset.uri = b.getAttribute('data-fedi-uri')||'';
|
|---|
| 251 | f.innerHTML = '<input type="text" autocomplete="off" spellcheck="false"><button type="submit" class="btn btn-primary fedi-remote-go" aria-label="ok">→</button><button type="button" class="fedi-remote-cancel" aria-label="x">×</button>';
|
|---|
| 252 | var _inp = f.querySelector('input'); _inp.placeholder = b.getAttribute('data-fedi-ph') || 'mastodon.social';
|
|---|
| 253 | try{ var _sv = localStorage.getItem('pcmsFediServer'); if(_sv) _inp.value = _sv; }catch(e){}
|
|---|
| 254 | document.body.appendChild(f); place(f, b); current=f; currentBtn=b; _inp.focus(); _inp.select();
|
|---|
| 255 | return;
|
|---|
| 256 | }
|
|---|
| 257 | if (current) close();
|
|---|
| 258 | });
|
|---|
| 259 | document.addEventListener('submit', function(e){ var f=e.target.closest && e.target.closest('.fedi-remote-form'); if(!f) return; e.preventDefault(); go(f.querySelector('input').value, f.dataset.uri); });
|
|---|
| 260 | document.addEventListener('keydown', function(e){ if (e.key === 'Escape') close(); });
|
|---|
| 261 | window.addEventListener('scroll', close, true);
|
|---|
| 262 | })();
|
|---|
| 263 |
|
|---|
| 264 | /* Like/Boost toggle in place — POST via fetch, flip the button, stay on the page (no reload, no banner). */
|
|---|
| 265 | (function(){
|
|---|
| 266 | if (window.__newsReactWired) return; window.__newsReactWired = true;
|
|---|
| 267 | document.addEventListener('submit', function(e){
|
|---|
| 268 | var f = e.target.closest && e.target.closest('.tl-react-form');
|
|---|
| 269 | if (!f) return;
|
|---|
| 270 | e.preventDefault();
|
|---|
| 271 | var btn = f.querySelector('button'); if (!btn || btn.disabled) return;
|
|---|
| 272 | btn.disabled = true;
|
|---|
| 273 | var body = new URLSearchParams();
|
|---|
| 274 | new FormData(f).forEach(function(v, k){ body.append(k, v); });
|
|---|
| 275 | fetch(f.action, { method: 'POST', body: body, headers: { 'X-Requested-With': 'fetch' }, credentials: 'same-origin' })
|
|---|
| 276 | .then(function(r){ return r.ok ? r.json() : null; })
|
|---|
| 277 | .then(function(j){ if (j) btn.classList.toggle('is-on', !!j.on); })
|
|---|
| 278 | .catch(function(){})
|
|---|
| 279 | .then(function(){ btn.disabled = false; });
|
|---|
| 280 | });
|
|---|
| 281 | })();
|
|---|
| 282 | </script>
|
|---|
| 283 |
|
|---|
| 284 | <script>
|
|---|
| 285 | // Collapse long post bodies to a max height with a "read more" toggle — only when the content
|
|---|
| 286 | // actually overflows. Runs on every /news render (full load + htmx swap); a per-element flag
|
|---|
| 287 | // prevents double-wiring.
|
|---|
| 288 | (function(){
|
|---|
| 289 | var RM = '<%= t("tl.read_more") %>', SL = '<%= t("tl.show_less") %>';
|
|---|
| 290 | document.querySelectorAll('.tl-content:not(.nsfw-media):not([data-rm])').forEach(function(c){
|
|---|
| 291 | c.setAttribute('data-rm', '1');
|
|---|
| 292 | if (c.scrollHeight > 360) {
|
|---|
| 293 | c.classList.add('tl-clamp');
|
|---|
| 294 | var b = document.createElement('button');
|
|---|
| 295 | b.type = 'button'; b.className = 'tl-readmore'; b.textContent = RM;
|
|---|
| 296 | b.addEventListener('click', function(){
|
|---|
| 297 | b.textContent = c.classList.toggle('tl-clamp') ? RM : SL;
|
|---|
| 298 | });
|
|---|
| 299 | c.insertAdjacentElement('afterend', b);
|
|---|
| 300 | }
|
|---|
| 301 | });
|
|---|
| 302 | })();
|
|---|
| 303 | </script>
|
|---|