Initial commit

This commit is contained in:
2024-09-26 01:55:31 +03:00
commit cb32524241
46 changed files with 5704 additions and 0 deletions

3
src/app.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

16
src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,16 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
import 'unplugin-icons/types/svelte'
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

12
src/app.html Normal file
View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

20
src/lib/en.json Normal file
View File

@@ -0,0 +1,20 @@
{
"anatoly_kopyl": "Anatoly Kopyl",
"hero": {
"subtitle": "Attention to&nbsp;detail in&nbsp;development of&nbsp;non-standard solutions of&nbsp;any complexity"
},
"projects": {
"h": "Projects",
"link_types": {
"website": "Website",
"chrome": "Chrome Web Store",
"github": "Github"
},
"pure_city": {
"name": "Pure City"
}
},
"footer": {
"made_with": "Made with SvelteKit and Tailwind"
}
}

View File

@@ -0,0 +1,3 @@
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" transform="scale(64)" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 973 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="7.025 7.025 497.951 497.95"><path d="M256 7.025C118.494 7.025 7.025 118.494 7.025 256S118.494 504.975 256 504.975 504.976 393.506 504.976 256C504.975 118.494 393.504 7.025 256 7.025zm-66.427 369.343h-54.665V199.761h54.665v176.607zM161.98 176.633c-17.853 0-32.326-14.591-32.326-32.587 0-17.998 14.475-32.588 32.326-32.588s32.324 14.59 32.324 32.588c.001 17.997-14.472 32.587-32.324 32.587zm232.45 199.735h-54.4v-92.704c0-25.426-9.658-39.619-29.763-39.619-21.881 0-33.312 14.782-33.312 39.619v92.704h-52.43V199.761h52.43v23.786s15.771-29.173 53.219-29.173c37.449 0 64.257 22.866 64.257 70.169l-.001 111.825z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 711 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 240.1 240.1">
<circle fill-rule="evenodd" clip-rule="evenodd" fill="white" cx="120.1" cy="120.1" r="120.1"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="black" d="M54.3,118.8c35-15.2,58.3-25.3,70-30.2 c33.3-13.9,40.3-16.3,44.8-16.4c1,0,3.2,0.2,4.7,1.4c1.2,1,1.5,2.3,1.7,3.3s0.4,3.1,0.2,4.7c-1.8,19-9.6,65.1-13.6,86.3 c-1.7,9-5,12-8.2,12.3c-7,0.6-12.3-4.6-19-9c-10.6-6.9-16.5-11.2-26.8-18c-11.9-7.8-4.2-12.1,2.6-19.1c1.8-1.8,32.5-29.8,33.1-32.3 c0.1-0.3,0.1-1.5-0.6-2.1c-0.7-0.6-1.7-0.4-2.5-0.2c-1.1,0.2-17.9,11.4-50.6,33.5c-4.8,3.3-9.1,4.9-13,4.8 c-4.3-0.1-12.5-2.4-18.7-4.4c-7.5-2.4-13.5-3.7-13-7.9C45.7,123.3,48.7,121.1,54.3,118.8z"/>
</svg>

After

Width:  |  Height:  |  Size: 748 B

45
src/lib/ru.json Normal file
View File

@@ -0,0 +1,45 @@
{
"anatoly_kopyl": "Анатолий Копыл",
"hero": {
"subtitle": "Разработка нестандартных решений любой сложности, внимание к&nbsp;деталям"
},
"projects": {
"h": "Проекты",
"link_types": {
"website": "Сайт",
"chrome": "Chrome Web Store",
"github": "Github"
},
"pure_city": {
"name": "Pure City",
"description": "Сайт клининговой компании с высокими стандартами и широким спектром услуг."
},
"vk_mute": {
"name": "VK Mute",
"description": "Расширение для Chromium позволяющее скрывать нежелательные сообщения в&nbsp;групповых чатах Вконтакте."
},
"vk_adult": {
"name": "VK Adult",
"description": "Расширение для Chromium возвращающее небезопасный поиск."
},
"tpk_artel": {
"name": "ТПК Артель",
"description": "Сайт компании предоставляющей строительные услуги, услуги по монтажу, техническому обслуживанию и множество других."
},
"games": {
"name": "Мои игры",
"description": "Сайт с играми, которые я разрабатывал в прошлой жизни используя Lua. Некоторые из них я транспилировал на js и сделал доступными для игры из браузера."
},
"musanthrope": {
"name": "Musanthrope x PAYDAY 2",
"description": "Лендинг мода для PAYDAY 2 с мешапами треков от Musanthrope."
},
"sleepy_care": {
"name": "Sleepy Care",
"description": "Факты о сне, генератор фонового шума с возможностью делиться и выбирать звуки от сообщества, журнал снов."
}
},
"footer": {
"made_with": "Сделано с SvelteKit и Tailwind"
}
}

22
src/lib/translations.ts Normal file
View File

@@ -0,0 +1,22 @@
import i18n from 'sveltekit-i18n';
const config = ({
loaders: [
{
locale: 'en',
key: '',
loader: async () => (
await import('./en.json')
).default,
},
{
locale: 'ru',
key: '',
loader: async () => (
await import('./ru.json')
).default,
}
],
});
export const { t, locale, locales, loading, loadTranslations } = new i18n(config);

23
src/routes/+layout.svelte Normal file
View File

@@ -0,0 +1,23 @@
<script lang="ts">
import "../app.css";
</script>
<slot />
<style lang="postcss">
:global(html) {
background: theme(colors.slate.950);
color: white;
}
:global(a) {
text-decoration: underline;
text-decoration-color: theme(colors.slate.500);
text-underline-offset: 4px;
transition: text-decoration-color .2s;
&:hover {
text-decoration-color: inherit;
}
}
</style>

13
src/routes/+layout.ts Normal file
View File

@@ -0,0 +1,13 @@
import type {LayoutLoad} from './$types';
import {loadTranslations} from "$lib/translations";
export const prerender = true;
export const load: LayoutLoad = async ({ url }) => {
const { pathname } = url;
const initLocale = 'ru';
await loadTranslations(initLocale, pathname);
return {};
}

13
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,13 @@
<script lang="ts">
import Navbar from "./Navbar.svelte";
import Hero from "./Hero.svelte";
import Projects from "./Projects/Projects.svelte";
import Footer from "./Footer.svelte";
</script>
<main>
<Navbar></Navbar>
<Hero></Hero>
<Projects></Projects>
<Footer></Footer>
</main>

16
src/routes/Footer.svelte Normal file
View File

@@ -0,0 +1,16 @@
<script lang="ts">
import {t} from "$lib/translations";
</script>
<footer class="border-t border-t-slate-800 p-4 md:px-8 flex gap-4 items-center justify-between">
<div class="text-sm text-slate-400">
{$t('footer.made_with')}
</div>
<a
class="text-sm text-slate-400"
href="https://v1.kopyl.dev"
>
Time Machine
</a>
</footer>

138
src/routes/Hero.svelte Normal file
View File

@@ -0,0 +1,138 @@
<script lang="ts">
import {onMount} from "svelte";
import {t} from "$lib/translations";
import Github from "$lib/icons/Github.svelte";
import Telegram from "$lib/icons/Telegram.svelte";
import Linkedin from "$lib/icons/Linkedin.svelte";
import MaterialSymbolsStarRounded from '~icons/material-symbols/star-rounded';
let logo: HTMLElement;
onMount(async () => {
const {gsap} = await import('gsap')
const {Flip} = await import('gsap/Flip')
const {ScrollTrigger} = await import('gsap/ScrollTrigger')
gsap.registerPlugin(Flip, ScrollTrigger);
ScrollTrigger.create({
trigger: logo,
start: "top-=10% top+=64px",
scrub: 1,
onEnter: () => {
const state = Flip.getState(logo);
logo.classList.add("hero__logo--in-nav")
Flip.from(state, {
duration: 1,
})
},
onLeaveBack: () => {
const state = Flip.getState(logo);
logo.classList.remove("hero__logo--in-nav")
Flip.from(state, {
duration: 1,
})
}
})
})
const goTop = () => {
document.body.scrollIntoView()
}
</script>
<div class="hero min-h-dvh flex flex-col justify-center gap-4 items-center p-2 md:p-8">
<div
class="hero__logo-wrapper mb-16"
>
<enhanced:img
src="$lib/assets/kopyl_frame_white.png"
alt="kopyl logo"
class="hero__logo"
bind:this={logo}
on:click={goTop}
on:keydown={goTop}
role="button"
tabindex="0"
></enhanced:img>
</div>
<h1 class="hero__name text-4xl text-center">
{$t('anatoly_kopyl')}
</h1>
<p class="text-center mb-8 text-slate-400 max-w-96 text-balance">
{@html $t('hero.subtitle')}
</p>
<div class="flex gap-6">
<a
class="hero__social-icon"
target="_blank"
href="https://github.com/anatolykopyl"
>
<Github></Github>
<div class="rounded-lg text-sm border border-slate-200 border-opacity-50 px-2 bg-slate-500 bg-opacity-25 absolute bottom-0 w-max translate-y-full mb-1 flex gap-1 items-center">
123
<MaterialSymbolsStarRounded class="max-h-4 max-w-4" />
</div>
</a>
<a
class="hero__social-icon"
target="_blank"
href="https://www.linkedin.com/in/akopyl/"
>
<Linkedin></Linkedin>
</a>
<a
class="hero__social-icon"
target="_blank"
href="https://t.me/anatolykopyl"
>
<Telegram></Telegram>
</a>
</div>
</div>
<style lang="postcss">
.hero__logo-wrapper {
min-width: 64px;
min-height: 64px;
width: 256px;
height: 256px;
}
.hero__logo {
box-sizing: border-box;
z-index: 100;
background: theme(colors.slate.950);
}
:global(.hero__logo--in-nav) {
position: fixed;
width: 64px;
height: 64px;
top: 16px;
left: 16px;
@media screen(md) {
left: 32px;
}
}
.hero__social-icon {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 4px;
height: fit-content;
:global(svg) {
width: 48px;
height: 48px;
color: white;
opacity: .8;
}
}
</style>

62
src/routes/Navbar.svelte Normal file
View File

@@ -0,0 +1,62 @@
<script lang="ts">
import MaterialSymbolsTranslateRounded from '~icons/material-symbols/translate-rounded';
import {locale} from "$lib/translations";
import {onMount} from "svelte";
let nav: HTMLElement;
const toggleLocale = () => {
$locale = $locale === 'ru' ? 'en' : 'ru'
}
onMount(async () => {
const {gsap} = await import('gsap')
const {ScrollTrigger} = await import('gsap/ScrollTrigger')
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({
trigger: '.hero__name',
start: "top top+=97px",
onEnter: () => {
nav.classList.add('nav--with-border')
},
onLeaveBack: () => {
nav.classList.remove('nav--with-border')
}
})
})
</script>
<nav
class="nav p-4 md:px-8 fixed top-0 w-full bg-slate-950 z-50 flex justify-between items-center"
bind:this={nav}
>
<div
class="nav__logo-spot"
></div>
<button
class="flex items-center justify-between gap-1 py-1 px-2 rounded border border-slate-600 h-fit w-16 transition hover:border-slate-400 uppercase"
on:click={toggleLocale}
>
<MaterialSymbolsTranslateRounded/>
{$locale}
</button>
</nav>
<style lang="postcss">
.nav {
border-bottom: 1px solid transparent;
transition: border-bottom-color .3s;
}
:global(.nav--with-border) {
border-bottom-color: theme(colors.slate.800) !important;
}
.nav__logo-spot {
width: 64px;
height: 64px;
}
</style>

View File

@@ -0,0 +1,65 @@
<script lang="ts">
import {t} from "$lib/translations";
import type {TProject} from "./projects";
export let project: TProject;
const visifyImage = (event: MouseEvent) => {
(event.target as HTMLElement).scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
}
</script>
<div class="relative rounded-2xl py-4 flex flex-col">
<div class="flex items-center gap-2 mb-2">
<h3 class="text-xl">
{$t(project.nameSlug)}
</h3>
<hr class="grow border-slate-800">
</div>
<p class="text-slate-400 mb-4 text-sm">
{@html $t(project.descriptionSlug)}
</p>
<div
class="project__gallery grid gap-1 grid-flow-col mb-4 mt-auto md:rounded"
>
{#each project.images as image}
<img
class="rounded"
alt={`${$t(project.nameSlug)} screenshot`}
src={image}
on:click={visifyImage}
>
{/each}
</div>
<div class="flex gap-4">
{#each project.links as link}
<a
target="_blank"
href={link.href}
class="flex gap-1 items-center py-1 text-sm"
>
{#if link.icon}
<svelte:component this={link.icon} />
{/if}
{$t(link.textSlug)}
</a>
{/each}
</div>
</div>
<style lang="postcss">
.project__gallery {
overflow: auto;
grid-auto-columns: calc(50% - 16px);
margin-inline: -8px;
padding-inline: 8px;
@media screen(md) {
margin-inline: 0;
padding-inline: 0;
}
}
</style>

View File

@@ -0,0 +1,19 @@
<script lang="ts">
import {t} from "$lib/translations";
import Project from "./Project.svelte";
import {projects} from "./projects";
</script>
<section class="px-2 md:px-8 mb-16 max-w-[1280px] mx-auto">
<h2 class="text-3xl mb-4 md:text-4xl">
{$t('projects.h')}
</h2>
<div class="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{#each projects as project}
<Project
project={project}
></Project>
{/each}
</div>
</section>

View File

@@ -0,0 +1,156 @@
import {SvelteComponent} from "svelte";
import MaterialSymbolsGlobe from '~icons/material-symbols/globe';
import MaterialSymbolsExtension from '~icons/material-symbols/extension';
import MdiGithub from '~icons/mdi/github';
import PureCityImg1 from "$lib/assets/projects/pure_city/1.png";
import PureCityImg2 from "$lib/assets/projects/pure_city/2.png";
import VkMuteImg1 from "$lib/assets/projects/vk_mute/1.png";
import TpkArtelImg1 from "$lib/assets/projects/tpk_artel/1.png";
import TpkArtelImg2 from "$lib/assets/projects/tpk_artel/2.png";
import TpkArtelImg3 from "$lib/assets/projects/tpk_artel/3.png";
import GamesImg1 from "$lib/assets/projects/games/1.png";
import GamesImg2 from "$lib/assets/projects/games/2.png";
import GamesImg3 from "$lib/assets/projects/games/3.png";
import MusanthropeImg1 from "$lib/assets/projects/musanthrope/1.png";
import MusanthropeImg2 from "$lib/assets/projects/musanthrope/2.png";
import SleepyCareImg1 from "$lib/assets/projects/sleepy_care/1.png";
import SleepyCareImg2 from "$lib/assets/projects/sleepy_care/2.png";
import SleepyCareImg3 from "$lib/assets/projects/sleepy_care/3.png";
import VkAdultImg1 from "$lib/assets/projects/vk_adult/1.png";
export type TProject = {
nameSlug: string
descriptionSlug: string
images: string[]
links: {
href: string
textSlug: string
icon?: SvelteComponent
}[]
}
export const projects = [
{
nameSlug: 'projects.pure_city.name',
descriptionSlug: 'projects.pure_city.description',
images: [
PureCityImg1,
PureCityImg2
],
links: [
{
href: 'https://pure-city.ru',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.vk_mute.name',
descriptionSlug: 'projects.vk_mute.description',
images: [
VkMuteImg1
],
links: [
{
href: 'https://chromewebstore.google.com/detail/vk-mute/mcnkfnjggkbenehgfelnnkklpkpjeibl',
textSlug: 'projects.link_types.chrome',
icon: MaterialSymbolsExtension
},
{
href: 'https://github.com/anatolykopyl/vk-mute',
textSlug: 'projects.link_types.github',
icon: MdiGithub
}
]
},
{
nameSlug: 'projects.tpk_artel.name',
descriptionSlug: 'projects.tpk_artel.description',
images: [
TpkArtelImg1,
TpkArtelImg2,
TpkArtelImg3
],
links: [
{
href: 'https://tpk-artel.ru',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.games.name',
descriptionSlug: 'projects.games.description',
images: [
GamesImg1,
GamesImg2,
GamesImg3
],
links: [
{
href: 'https://games.kopyl.dev',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.musanthrope.name',
descriptionSlug: 'projects.musanthrope.description',
images: [
MusanthropeImg1,
MusanthropeImg2
],
links: [
{
href: 'https://musanthrope.kopyl.dev',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.vk_adult.name',
descriptionSlug: 'projects.vk_adult.description',
images: [
VkAdultImg1
],
links: [
{
href: 'https://chromewebstore.google.com/detail/vk-adult/laiidhgadnejimjkaobcecboppgbeppa',
textSlug: 'projects.link_types.chrome',
icon: MaterialSymbolsExtension
},
{
href: 'https://github.com/anatolykopyl/vk-adult',
textSlug: 'projects.link_types.github',
icon: MdiGithub
},
{
href: 'https://vk-adult.kopyl.dev',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
},
{
nameSlug: 'projects.sleepy_care.name',
descriptionSlug: 'projects.sleepy_care.description',
images: [
SleepyCareImg1,
SleepyCareImg2,
SleepyCareImg3
],
links: [
{
href: 'https://sleepy.care',
textSlug: 'projects.link_types.website',
icon: MaterialSymbolsGlobe
}
]
}
] as TProject[]