Show a picture whose address was typed without a scheme
CI / Typecheck, test, build (pull_request) Successful in 26s

`preview.redd.it/lz4drsqh0clh1.jpeg?width=1290&...` was rendering as plain
text -- not a picture, and not even a link. The bare-address rule has always
insisted on `https://`, so anything copied out of an address bar, where the
browser hides the scheme, fell through to nothing at all. That predates the
inline images from the last commit; it just did not matter until pictures
started being worth placing.

The rule is deliberately narrow: a host with a dot, a path, and an image
extension. Widening it to every schemeless address would be the obvious move
and is wrong, because a comment thread is full of dotted, slashed prose --
`src/render/post.ts` would become a link to a website in Tonga, and
`node_modules/foo/bar.js` a website in Jersey. Requiring the extension costs
nothing here, since the only thing worth guessing a scheme for is a picture.

https is assumed. Every host that serves these redirects to it anyway.

Checked both ways: the four address shapes that should become pictures do,
and nine pieces of ordinary prose that must not -- file paths, a Windows
drive letter, a relative path, an email address followed by a filename, a
version number -- still do not.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017nMQ2eDKnqALYhAibpTKTu
This commit is contained in:
2026-08-27 16:19:25 -03:00
co-authored by Claude Opus 5
parent 0db18547c8
commit 031101c382
3 changed files with 48 additions and 4 deletions
+13 -3
View File
@@ -69,7 +69,12 @@ const INLINE = new RegExp(
'(?<![\\w*])\\*([^*\\n]+)\\*(?![\\w*])', // 8 em with asterisks
'(?<![\\w_])_([^_\\n]+)_(?![\\w_])', // 9 em with underscores
'(https?://[^\\s<>]+)', // 10 bare url
'(?<![\\w/])(/?[ru]/[A-Za-z0-9_][A-Za-z0-9_-]{1,30})', // 11 r/sub and u/name
// 11 the same thing with the scheme left off, which is how people type
// them. Narrow on purpose: a host, a path, and an image extension. Prose
// is full of dotted words, and `src/render/post.ts` must not become a
// link to a website in Tonga.
'(?<![\\w@/.])((?:[a-z0-9-]+\\.)+[a-z]{2,}/[^\\s<>]*\\.(?:jpe?g|png|gif|webp|avif)(?:\\?[^\\s<>]*)?)',
'(?<![\\w/])(/?[ru]/[A-Za-z0-9_][A-Za-z0-9_-]{1,30})', // 12 r/sub and u/name
].join('|'),
'g',
);
@@ -80,8 +85,8 @@ function inline(text: string, image?: ImageRenderer): string {
let cursor = 0;
for (const match of text.matchAll(INLINE)) {
const [whole, code, alt, src, label, href, strong, strike, emStar, emScore, url, subOrUser] =
match;
const [whole, code, alt, src, label, href, strong, strike, emStar, emScore, url, schemeless,
subOrUser] = match;
out += escapeHtml(text.slice(cursor, match.index));
cursor = match.index + whole.length;
@@ -113,6 +118,11 @@ function inline(text: string, image?: ImageRenderer): string {
} else {
out += anchor(safe, trimmed.replace(/^https?:\/\/(www\.)?/, '')) + tail;
}
} else if (schemeless !== undefined) {
// Assumed https: every host that serves these redirects to it anyway,
// and a picture is the one thing worth guessing a scheme for.
const safe = safeHref(`https://${schemeless}`);
out += safe ? (image ? image(safe, '') : anchor(safe, schemeless)) : escapeHtml(whole);
} else if (subOrUser !== undefined) {
const path = subOrUser.startsWith('/') ? subOrUser : `/${subOrUser}`;
out += anchor(`${REDDIT}${path}`, subOrUser);