Переработал обработчик иконок для гиперссылок

This commit is contained in:
Иван Кузьменко 2025-07-05 05:57:27 +03:00
parent 418892b3a4
commit df54426a75
5 changed files with 143 additions and 104 deletions

View file

@ -1,57 +1,11 @@
<script lang="ts">
import { tryGetIcon } from '$lib/util/IconResolver';
import HoverIcon from './HoverIcon.svelte';
export let href: string;
let className: string = '';
export { className as class };
export let customIcon: string | null = null;
let url: URL;
let host: string;
const icons: Record<string, string> = {
none: 'material-symbols:link',
'steamcommunity.com': 'simple-icons:steam',
'twitter.com': 'simple-icons:x',
'x.com': 'simple-icons:x',
'github.com': 'simple-icons:github',
'youtube.com': 'simple-icons:youtube',
'itch.io': 'simple-icons:itchdotio',
'discord.gg': 'simple-icons:discord',
'gamebanana.com': 'simple-icons:gamebanana',
// https://хамяк.рф
'xn--80auf8a2c.xn--p1ai': 'fluent-emoji-high-contrast:hamster',
'teasanctuary.ru': '/icons/tea-sanctuary.svg',
'hl.teasanctuary.ru': '/icons/half-life.svg',
localhost: '/icons/tea-sanctuary.svg',
email: 'material-symbols:alternate-email'
};
function getHost(url: URL) {
const isEmail = url.href.startsWith('mailto:');
const hostname = isEmail ? 'email' : url.hostname;
// const split = hostname.split('.');
// const name = split[Math.max(split.length - 2, 0)];
// if (split.length > 1) return `${name}.${split[split.length - 1]}`;
// return name;
return hostname;
}
function tryGetIcon(link: string) {
try {
url = new URL(link);
host = getHost(url).replace(/\.com$/, '');
} catch {
return icons['none'];
}
let domain = getHost(url).toLocaleLowerCase();
let key = Object.keys(icons).find((key) => key == domain);
if (key == null) return icons['none'];
return icons[key];
}
</script>
<a
@ -60,7 +14,7 @@
target="_blank"
>
<div class="shrink-0 rounded-l-xl bg-slate-800 p-2">
<HoverIcon src={customIcon ?? tryGetIcon(href)} class="text-sm uppercase" text={host} />
<HoverIcon src={customIcon ?? tryGetIcon(href)} class="text-sm uppercase" text={href} />
</div>
<div
class="flex shrink-0 grow flex-nowrap items-center justify-center rounded-r-xl bg-slate-100 p-2 text-2xl text-nowrap text-slate-950"

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { MediaQuery } from 'svelte/reactivity';
import HoverIcon from './HoverIcon.svelte';
import { tryGetIcon } from '$lib/util/IconResolver';
export let href: string;
let className: string = '';
@ -8,60 +9,13 @@
export let customIcon: string | null = null;
const sm = new MediaQuery('width >= 40rem', false);
let url: URL;
let host: string;
const icons: Record<string, string> = {
none: 'material-symbols:link',
'steamcommunity.com': 'simple-icons:steam',
'twitter.com': 'simple-icons:x',
'x.com': 'simple-icons:x',
'github.com': 'simple-icons:github',
'youtube.com': 'simple-icons:youtube',
'itch.io': 'simple-icons:itchdotio',
'discord.gg': 'simple-icons:discord',
'gamebanana.com': 'simple-icons:gamebanana',
// https://хамяк.рф
'xn--80auf8a2c.xn--p1ai': 'fluent-emoji-high-contrast:hamster',
'teasanctuary.ru': '/icons/tea-sanctuary.svg',
'hl.teasanctuary.ru': '/icons/half-life.svg',
localhost: '/icons/tea-sanctuary.svg',
email: 'material-symbols:alternate-email'
};
function getHost(url: URL) {
const isEmail = url.href.startsWith('mailto:');
const hostname = isEmail ? 'email' : url.hostname;
// const split = hostname.split('.');
// const name = split[Math.max(split.length - 2, 0)];
// if (split.length > 1) return `${name}.${split[split.length - 1]}`;
// return name;
return hostname;
}
function tryGetIcon(link: string) {
try {
url = new URL(link);
host = getHost(url).replace(/\.com$/, '');
} catch {
return icons['none'];
}
let domain = getHost(url).toLocaleLowerCase();
let key = Object.keys(icons).find((key) => key == domain);
if (key == null) return icons['none'];
return icons[key];
}
</script>
<a {href} class="{className} inline-block group" target="_blank">
<span class="inline-block {sm.current ? 'size-8 rounded-xl p-1' : 'size-6 rounded-sm p-0.5'} bg-emerald-800 align-bottom transition-all group-hover:scale-110">
<HoverIcon
src={customIcon ?? tryGetIcon(href)}
text={host}
text={href}
size={sm.current ? 24 : 20}
/>
</span>

View file

@ -0,0 +1,65 @@
const icons: Record<string, string> = {
none: 'material-symbols:link',
'steamcommunity.com': 'simple-icons:steam',
'twitter.com': 'simple-icons:x',
'x.com': 'simple-icons:x',
'github.com': 'simple-icons:github',
'youtube.com': 'simple-icons:youtube',
'itch.io': 'simple-icons:itchdotio',
'discord.gg': 'simple-icons:discord',
'gamebanana.com': 'simple-icons:gamebanana',
'bsky.app': 'simple-icons:bluesky',
'bsky.social': 'simple-icons:bluesky',
// https://хамяк.рф
'xn--80auf8a2c.xn--p1ai': 'fluent-emoji-high-contrast:hamster',
'teasanctuary.ru': '/icons/tea-sanctuary-white.svg',
'hl.teasanctuary.ru': '/icons/half-life.svg',
'git.teasanctuary.ru': 'devicon-plain:git',
localhost: '/icons/tea-sanctuary-white.svg',
email: 'material-symbols:alternate-email'
};
// Особые случаи, когда одним доменом второго уровня не ограничишься (например, randomtrash.itch.io)
const specialResolvers: Record<string, (url: URL) => string> = {
'teasanctuary.ru': (url) => {
// Домены третьего уровня и выше
const prefix = url.hostname.split('.').toReversed();
prefix.shift();
prefix.shift();
if (prefix[0] === "hl") {
return 'hl.teasanctuary.ru';
}
if (prefix[0] === "git" || url.pathname.split('/')[1] == "git") {
return 'git.teasanctuary.ru';
}
return 'teasanctuary.ru';
},
// Игнорируем имя пользователя
'bsky.app': (url) => 'bsky.app',
'bsky.social': (url) => 'bsky.social',
'itch.io': (url) => 'itch.io'
}
function getIconFromUrl(url: URL): string | undefined {
const href = url.href;
if (href.startsWith('mailto:'))
return 'email';
const hostname = url.hostname;
const secondLevel = hostname.match(/(([A-Za-z0-9\-])+\.([A-Za-z0-9\-])+)$/)?.at(0) ?? '';
if (specialResolvers[secondLevel])
return icons[specialResolvers[secondLevel](url)];
return icons[hostname];
}
export function tryGetIcon(link: string): string {
let url: URL;
try {
url = new URL(link);
} catch {
return icons['none'];
}
return getIconFromUrl(url) ?? icons['none'];
}

View file

@ -72,18 +72,14 @@
<SocialButton class="w-60 shrink-0" href="https://github.com/TeaSanctuary/">
GitHub
</SocialButton>
<SocialButton
class="w-60 shrink-0"
href="https://teasanctuary.ru/git"
customIcon="devicon-plain:git"
>
<SocialButton class="w-60 shrink-0" href="https://teasanctuary.ru/git">
Наш Git
</SocialButton>
<SocialButton class="w-60 shrink-0" href="https://hl.teasanctuary.ru">
Сервер HLDM
</SocialButton>
</div>
<SocialButton class="w-60 opacity-0 hover:opacity-100" href="https://хамяк.рф">
<SocialButton class="w-60 opacity-1 hover:opacity-100" href="https://хамяк.рф">
хамяк)
</SocialButton>
</div>
@ -111,8 +107,8 @@
<div class="text-justify">
Наша главная страсть &mdash; это, конечно, видеоигры. Мы часто участвуем в так называемых
"гейм джемах" &mdash; конкурсах на разработку игр. Наши игры вы можете оценить здесь:
<SocialHyperlink customIcon="simple-icons:itchdotio" href="https://randomtrash.itch.io">RandomTrash</SocialHyperlink>
<SocialHyperlink customIcon="simple-icons:itchdotio" href="https://friendlywithmeat.itch.io/">FriendlyWithMeat</SocialHyperlink>.
<SocialHyperlink href="https://randomtrash.itch.io">RandomTrash</SocialHyperlink>
<SocialHyperlink href="https://friendlywithmeat.itch.io/">FriendlyWithMeat</SocialHyperlink>.
Также мы ведём работу над нашим первым полноценным игровым проектом.
Следите за новостями в нашем
<SocialHyperlink href={PUBLIC_TS_DISCORD}>сообществе</SocialHyperlink>!