Changeset ef1853c in Klonkt
- Timestamp:
- 07/26/2026 09:52:24 AM (6 weeks ago)
- Branches:
- main
- Children:
- 42e7616
- Parents:
- 3597763
- Location:
- src
- Files:
-
- 6 edited
-
config/database.js (modified) (1 diff)
-
services/ActivityPubService.js (modified) (8 diffs)
-
views/pages/news.ejs (modified) (1 diff)
-
views/partials/fedi-node.ejs (modified) (1 diff)
-
views/partials/msg-item.ejs (modified) (2 diffs)
-
views/partials/shared-styles.ejs (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/config/database.js
r3597763 ref1853c 571 571 // context instead. Existing rows default to 'public' (historically almost all were). 572 572 ensureColumn('ap_interactions', 'visibility', "TEXT DEFAULT 'public'"); 573 ensureColumn('ap_interactions', 'emoji_json', 'TEXT'); // FEP-9098 custom emojis in a reply's content (messages + thread) 574 ensureColumn('ap_interactions', 'actor_emoji_json', 'TEXT'); // FEP-9098 custom emojis in the reply author's display name 573 575 // Rich replies: the reply's language (BCP47 code) → contentMap on the outgoing Note. 574 576 ensureColumn('ap_outbox', 'language', 'TEXT'); -
src/services/ActivityPubService.js
r3597763 ref1853c 825 825 function iStmts() { 826 826 if (!_insI) { 827 _insI = db.prepare('INSERT OR IGNORE INTO ap_interactions (kind, post_id, object_uri, actor_uri, actor_name, actor_handle, actor_url, actor_icon, content, published, parent_uri, visibility, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)');827 _insI = db.prepare('INSERT OR IGNORE INTO ap_interactions (kind, post_id, object_uri, actor_uri, actor_name, actor_handle, actor_url, actor_icon, content, published, parent_uri, visibility, emoji_json, actor_emoji_json, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)'); 828 828 _delLA = db.prepare('DELETE FROM ap_interactions WHERE kind = ? AND post_id = ? AND actor_uri = ?'); 829 829 _delReply = db.prepare("DELETE FROM ap_interactions WHERE kind = 'reply' AND object_uri = ?"); 830 _listI = db.prepare('SELECT id, kind, object_uri, parent_uri, actor_uri, actor_name, actor_handle, actor_url, actor_icon, content, published, created_at, acted_boost, acted_like, visibility FROM ap_interactions WHERE post_id = ? ORDER BY created_at ASC');830 _listI = db.prepare('SELECT id, kind, object_uri, parent_uri, actor_uri, actor_name, actor_handle, actor_url, actor_icon, content, published, created_at, acted_boost, acted_like, visibility, emoji_json, actor_emoji_json FROM ap_interactions WHERE post_id = ? ORDER BY created_at ASC'); 831 831 _getI = db.prepare('SELECT * FROM ap_interactions WHERE id = ?'); 832 832 _insO = db.prepare('INSERT INTO ap_outbox (id, site_slug, post_id, post_slug, in_reply_to, to_actor, to_handle, content, language, attachments, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,CURRENT_TIMESTAMP)'); … … 945 945 actor_name: r.actor_name, actor_handle: r.actor_handle, actor_url: r.actor_url, 946 946 actor_icon: r.actor_icon, content: stripLeadingMentions(r.content), created_at: r.published || r.created_at, 947 emoji_json: r.emoji_json, actor_emoji_json: r.actor_emoji_json, // FEP-9098 (thread render) 947 948 acted_boost: !!r.acted_boost, acted_like: !!r.acted_like, 948 949 children: [], … … 1512 1513 const html = HtmlSanitizerService.sanitize(o.content || ''); 1513 1514 if (isRejectedObject(o.id)) { console.log('[AP] reply skipped (tombstoned)', o.id); return 202; } 1514 iStmts().ins.run('reply', tgt.post_id, o.id || '', actorUri, ai.name, ai.handle, ai.url, ai.icon, html, o.published || null, tgt.parent_uri, noteVisibility(o) );1515 iStmts().ins.run('reply', tgt.post_id, o.id || '', actorUri, ai.name, ai.handle, ai.url, ai.icon, html, o.published || null, tgt.parent_uri, noteVisibility(o), extractEmojiTags(o.tag), emojiJsonOf(ai.emojis)); 1515 1516 console.log('[AP] reply', actorUri, '→', tgt.post_id); 1516 1517 { … … 1657 1658 } 1658 1659 const ai = actorInfo(await resolveActor(actorUri), actorUri); 1659 iStmts().ins.run(type.toLowerCase(), pid, '', actorUri, ai.name, ai.handle, ai.url, ai.icon, null, null, null, noteVisibility(act) );1660 iStmts().ins.run(type.toLowerCase(), pid, '', actorUri, ai.name, ai.handle, ai.url, ai.icon, null, null, null, noteVisibility(act), null, emojiJsonOf(ai.emojis)); 1660 1661 console.log('[AP]', type === 'Like' ? 'like' : 'boost', actorUri, '→', pid); 1661 1662 { … … 2707 2708 } 2708 2709 2710 // A display-name emoji map (actorInfo().emojis) → JSON to store, or null. 2711 function emojiJsonOf(map) { return (map && Object.keys(map).length) ? JSON.stringify(map) : null; } 2712 2709 2713 // ── Cirkel = posts from the accounts you auto-boost ("feature an artist") ── 2710 2714 let _abCount, _cirkelPosts, _cirkelMembers; … … 2996 3000 const html = HtmlSanitizerService.sanitize(child.content || ''); 2997 3001 // The child replies to `note` by construction (it's in note's replies collection). 2998 try { iStmts().ins.run('reply', postId, child.id, actorUri, ai.name, ai.handle, ai.url, ai.icon, html, child.published || null, note.id || noteUri, noteVisibility(child) ); added++; } catch { /* ignore */ }3002 try { iStmts().ins.run('reply', postId, child.id, actorUri, ai.name, ai.handle, ai.url, ai.icon, html, child.published || null, note.id || noteUri, noteVisibility(child), extractEmojiTags(child.tag), emojiJsonOf(ai.emojis)); added++; } catch { /* ignore */ } 2999 3003 nextFrontier.push(child.id); // expand this reply's own replies next depth 3000 3004 } … … 3309 3313 const rows = db.prepare(` 3310 3314 SELECT i.kind, i.actor_name, i.actor_handle, i.actor_url, i.actor_icon, i.content, i.created_at, i.visibility, 3315 i.emoji_json, i.actor_emoji_json, 3311 3316 p.slug AS post_slug, p.title AS post_title 3312 3317 FROM ap_interactions i LEFT JOIN posts p ON p.id = i.post_id … … 3317 3322 type: r.kind, name: r.actor_name, handle: r.actor_handle, url: r.actor_url, icon: r.actor_icon, 3318 3323 content: stripLeadingMentions(r.content), post_slug: r.post_slug, post_title: r.post_title, created_at: r.created_at, 3324 emoji_json: r.emoji_json, actor_emoji_json: r.actor_emoji_json, // FEP-9098 (messages render) 3319 3325 // followers/direct = a private message to the owner (not on the public thread) → 🔒 in Messages 3320 3326 visibility: r.visibility || 'public', -
src/views/pages/news.ejs
r3597763 ref1853c 137 137 .tl-readmore:hover { text-decoration: underline; } 138 138 139 /* FEP-9098 custom emojis: small inline images, never treated as media. */140 img.emoji { height: 1.35em; width: auto; margin: 0 .04em; vertical-align: -0.22em;141 display: inline-block; border-radius: 0; background: none; box-shadow: none; }142 .tl-content img.emoji, .tl-quote-body img.emoji { max-width: none; }143 144 139 /* FEP-044f embedded quote card (mirror of the Shaer QuoteCard). */ 145 140 .tl-quote { margin: .75rem 0 0; padding: .6rem .7rem; border-radius: 12px; -
src/views/partials/fedi-node.ejs
r3597763 ref1853c 6 6 <div class="comment-body"> 7 7 <div class="comment-meta"> 8 <% if (n.actor_url) { %><a class="comment-author" href="<%= n.actor_url %>" rel="nofollow noopener" target="_blank"><% = n.actor_name%></a>9 <% } else { %><span class="comment-author"><% = n.actor_name%></span><% } %>8 <% if (n.actor_url) { %><a class="comment-author" href="<%= n.actor_url %>" rel="nofollow noopener" target="_blank"><%- emojiName(n.actor_name, n.actor_emoji_json) %></a> 9 <% } else { %><span class="comment-author"><%- emojiName(n.actor_name, n.actor_emoji_json) %></span><% } %> 10 10 <% if (n.actor_handle) { %><span class="fedi-handle"><%= n.actor_handle %></span><% } %> 11 11 <% if (n.created_at) { %><span class="comment-time"><%= formatDateTime(n.created_at) %></span><% } %> 12 12 </div> 13 <div class="comment-content"><%- n.content%></div>13 <div class="comment-content"><%- emojiHtml(n.content, n.emoji_json) %></div> 14 14 <%# Rich replies: media on our own sent replies (attachments on the Note). 15 15 Visitors see these too, and the reply editor (owner-only) may not render, -
src/views/partials/msg-item.ejs
r3597763 ref1853c 40 40 <% if (n.to_handle) { %><span class="msg-handle">→ <%= n.to_handle %></span><% } %> 41 41 <% } else { %> 42 <a class="msg-who" href="<%= n.url %>" target="_blank" rel="nofollow noopener"><% = _who%></a>42 <a class="msg-who" href="<%= n.url %>" target="_blank" rel="nofollow noopener"><%- emojiName(_who, n.actor_emoji_json) %></a> 43 43 <% if (n.handle && n.name) { %><span class="msg-handle"><%= n.handle %></span><% } %> 44 44 <% } %> … … 67 67 68 68 <% if (_t === 'report' && n.content) { %><div class="msg-content"><%= n.content %></div> 69 <% } else if ((_t === 'reply' || _t === 'mention' || _t === 'sent') && n.content) { %><div class="msg-content"><%- n.content%></div>69 <% } else if ((_t === 'reply' || _t === 'mention' || _t === 'sent') && n.content) { %><div class="msg-content"><%- emojiHtml(n.content, n.emoji_json) %></div> 70 70 <% if (_t === 'mention' && n.wave && n.actorUri && canMutate) { %> 71 71 <form class="msg-quickreply" method="post" action="<%= (typeof siteUrlBase !== 'undefined' ? siteUrlBase : '') %>/messages/quick-reply" style="display:flex;gap:6px;flex-wrap:wrap;margin-top:6px"> -
src/views/partials/shared-styles.ejs
r3597763 ref1853c 193 193 .load-more-btn:hover { background: color-mix(in srgb, var(--ink, #000) 9%, transparent); } 194 194 .load-more-btn.htmx-request { opacity: .6; pointer-events: none; } 195 196 /* FEP-9098 custom emojis: small inline images (Shaer parity), rendered in note 197 content and display names across the timeline, messages and threads. Never 198 styled as media (no rounding/background), and never blown up to media width. */ 199 img.emoji { height: 1.3em; width: auto; margin: 0 .05em; vertical-align: -0.2em; 200 display: inline-block; border-radius: 0; background: none; box-shadow: none; } 201 .tl-content img.emoji, .tl-quote-body img.emoji, .msg-content img.emoji, .comment-content img.emoji, 202 .tl-author img.emoji, .tl-boost-by img.emoji, .tl-quote-name img.emoji, .msg-who img.emoji, .comment-author img.emoji { 203 max-width: none; height: 1.3em; border-radius: 0; background: none; box-shadow: none; vertical-align: -0.2em; } 195 204 </style>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)