Chat+ link previews: a link in a chat shows its page's title, text and picture, YouTube playable in place

X's web chat shows a link as a bare card. An X tab on a chat page may now ask native code
(link_preview, x-capture only, chat pages only) for one link's preview. Native code fetches it:
https and public hosts only (addresses checked and pinned), never X's hosts, no cookies or
referrer, redirects re-checked, page head <= 512 KB, images <= 1.5 MB by magic bytes as data:,
30 fetches a minute, memory-only cache. Only the link leaves the page. On by default (owner,
2026-10-10).
This commit is contained in:
CRTkafa 2026-10-10 21:47:31 +08:00
parent 96f909c110
commit 328264bfe1
21 changed files with 1455 additions and 519 deletions

View file

@ -751,6 +751,10 @@
"plugin.chat-plus.bookmarks.importBad": "This is not an export of starred messages.",
"plugin.chat-plus.bookmarks.imported": "{count, plural, =0 {Nothing new to import} one {# starred message imported} other {# starred messages imported}}",
"plugin.chat-plus.bookmarks.close": "Close",
"plugin.chat-plus.feature.linkPreviews.label": "Link previews",
"plugin.chat-plus.feature.linkPreviews.description": "Shows a preview under a link in a chat: the page's title, text and picture, and for YouTube the video, playable in place. To make one, the app asks the link's site for its preview itself: only the link is sent, without cookies, never the message or who sent it. Nothing is saved.",
"plugin.chat-plus.linkPreviews.play": "Play the video here",
"plugin.chat-plus.linkPreviews.open": "Open the link",
"plugin.chat-plus.feature.searchLoaded.label": "Find in conversation",
"plugin.chat-plus.feature.searchLoaded.description": "Ctrl+Shift+F in a chat searches the messages X has loaded so far and jumps to each match. It can load older messages while it is open.",
"plugin.chat-plus.searchLoaded.title": "Find in conversation",

View file

@ -17,7 +17,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ar-x-fm",
@ -28,7 +28,7 @@
"tier": "community",
"reviewers": [],
"fallback": "ar",
"coverage": 1
"coverage": 0.996
},
{
"code": "bn",
@ -38,7 +38,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "eu",
@ -48,7 +48,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "en-GB",
@ -69,7 +69,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ca",
@ -79,7 +79,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "zh-Hans",
@ -89,7 +89,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "zh-Hant",
@ -99,7 +99,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "hr",
@ -109,7 +109,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "cs",
@ -119,7 +119,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "da",
@ -129,7 +129,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "nl",
@ -139,7 +139,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "fil",
@ -149,7 +149,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "fi",
@ -159,7 +159,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "fr",
@ -169,7 +169,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "gl",
@ -179,7 +179,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "de",
@ -189,7 +189,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "el",
@ -199,7 +199,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "gu",
@ -209,7 +209,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ha",
@ -219,7 +219,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "he",
@ -229,7 +229,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "hi",
@ -239,7 +239,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "hu",
@ -249,7 +249,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ig",
@ -259,7 +259,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "id",
@ -269,7 +269,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ga",
@ -279,7 +279,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "it",
@ -289,7 +289,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ja",
@ -299,7 +299,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "kn",
@ -309,7 +309,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ko",
@ -319,7 +319,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ms",
@ -329,7 +329,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "mr",
@ -339,7 +339,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "nb",
@ -349,7 +349,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "fa",
@ -359,7 +359,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "pl",
@ -369,7 +369,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "pt",
@ -379,7 +379,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ro",
@ -389,7 +389,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ru",
@ -399,7 +399,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "sr",
@ -409,7 +409,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "sk",
@ -419,7 +419,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "es",
@ -429,7 +429,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "sv",
@ -439,7 +439,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ta",
@ -449,7 +449,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "th",
@ -459,7 +459,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "tr",
@ -479,7 +479,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "ur",
@ -489,7 +489,7 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "vi",
@ -499,7 +499,7 @@
"status": "machine",
"tier": "supported",
"reviewers": [],
"coverage": 1
"coverage": 0.996
},
{
"code": "yo",
@ -509,6 +509,6 @@
"status": "machine",
"tier": "community",
"reviewers": [],
"coverage": 1
"coverage": 0.996
}
]

View file

@ -800,6 +800,10 @@
"plugin.chat-plus.personFilter.groupOnly": "Önce bir grup sohbeti aç",
"plugin.chat-plus.personFilter.nobody": "Henüz seçilecek kimse yok: hiç mesaj yüklenmedi.",
"plugin.chat-plus.personFilter.hint": "Şimdiye kadar yüklenen mesajları yazan kişiler. Daha fazlası için yukarı kaydır.",
"plugin.chat-plus.feature.linkPreviews.label": "Link önizlemeleri",
"plugin.chat-plus.feature.linkPreviews.description": "Sohbetteki bir linkin altında önizlemesini gösterir: sayfanın başlığı, metni ve görseli; YouTube'da ise yerinde oynatılabilen video. Önizleme için uygulama linkin sitesine kendisi sorar: yalnızca link gider, çerezsiz; mesaj ya da gönderen asla gitmez. Hiçbir şey kaydedilmez.",
"plugin.chat-plus.linkPreviews.play": "Videoyu burada oynat",
"plugin.chat-plus.linkPreviews.open": "Linki aç",
"plugin.chat-plus.feature.keywords.label": "Sohbetlerde anahtar kelimeler",
"plugin.chat-plus.feature.keywords.description": "Kelimelerini içeren sohbet mesajlarını vurgular, sessize aldığın kelimeleri içeren mesajları katlar. Katlanmış bir mesaj tıklayınca açılır.",
"plugin.chat-plus.keywords.highlight": "Vurgulanacak kelimeler",