feat(managers): clickable links in descriptions + real SVG country flags

- linkify: bare http(s) URLs inside channel descriptions become clickable links
  (new tab, rel=noopener noreferrer) — new lib/linkify.tsx, used in the shared
  ChannelAboutContent so both the About overlay and the channel detail page get it
- Country flag: the flag EMOJI never renders on Windows/Chrome (showed the small-caps
  'hu' fallback), so swap it for a real self-hosted SVG flag via the flag-icons package;
  applies to the About overlay + the channel detail page
This commit is contained in:
2026-07-19 20:42:28 +02:00
parent 8abb78b0bd
commit b741a8fd28
5 changed files with 53 additions and 8 deletions
+36
View File
@@ -0,0 +1,36 @@
import { Fragment, type ReactNode } from "react";
// A bare http(s) URL: greedy up to whitespace/<, but don't swallow a trailing sentence
// punctuation mark (so "…see https://x.com/y." links "https://x.com/y", not the dot).
const URL_RE = /(https?:\/\/[^\s<]+[^\s<.,!?;:)\]}"'])/g;
/**
* Turn bare http(s) URLs inside a plain-text string into clickable links that open in a new tab
* (with a safe rel). Everything else is returned as text, so it composes with `whitespace-pre-wrap`
* (line breaks preserved). Used for channel/video descriptions where links arrive as plain text.
*/
export function linkify(text: string): ReactNode[] {
const out: ReactNode[] = [];
let last = 0;
let key = 0;
URL_RE.lastIndex = 0;
let m: RegExpExecArray | null;
while ((m = URL_RE.exec(text)) !== null) {
if (m.index > last) out.push(<Fragment key={key++}>{text.slice(last, m.index)}</Fragment>);
const url = m[0];
out.push(
<a
key={key++}
href={url}
target="_blank"
rel="noopener noreferrer"
className="text-accent hover:underline break-all"
>
{url}
</a>,
);
last = m.index + url.length;
}
if (last < text.length) out.push(<Fragment key={key++}>{text.slice(last)}</Fragment>);
return out;
}