mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 18:39:14 +03:00
replaced with markdown-to
This commit is contained in:
parent
838a74c086
commit
00d32bb002
0
.dockerfile
Normal file
0
.dockerfile
Normal file
0
.dockerignore
Normal file
0
.dockerignore
Normal file
@ -14,6 +14,7 @@ export default defineConfig({
|
|||||||
output: "server",
|
output: "server",
|
||||||
server: {
|
server: {
|
||||||
port: 9966,
|
port: 9966,
|
||||||
|
host: true,
|
||||||
allowedHosts: ["showcase.yarkov.tech"],
|
allowedHosts: ["showcase.yarkov.tech"],
|
||||||
},
|
},
|
||||||
adapter: node({
|
adapter: node({
|
||||||
|
|||||||
0
compose.yml
Normal file
0
compose.yml
Normal file
0
nginx/website.conf
Normal file
0
nginx/website.conf
Normal file
1343
package-lock.json
generated
1343
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@ -11,8 +11,8 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev --host",
|
"dev": "astro dev",
|
||||||
"build": "astro check && astro build",
|
"build": "astro build",
|
||||||
"start": "node --env-file=.env ./dist/server/entry.mjs",
|
"start": "node --env-file=.env ./dist/server/entry.mjs",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
"astro": "astro",
|
"astro": "astro",
|
||||||
@ -25,6 +25,7 @@
|
|||||||
"@astrojs/rss": "^4.0.13",
|
"@astrojs/rss": "^4.0.13",
|
||||||
"@astrojs/solid-js": "^5.1.3",
|
"@astrojs/solid-js": "^5.1.3",
|
||||||
"@iconify-json/mdi": "^1.2.3",
|
"@iconify-json/mdi": "^1.2.3",
|
||||||
|
"@types/markdown-it": "^14.1.2",
|
||||||
"astro": "^5.15.8",
|
"astro": "^5.15.8",
|
||||||
"astro-icon": "^1.1.5",
|
"astro-icon": "^1.1.5",
|
||||||
"graphql": "^16.12.0",
|
"graphql": "^16.12.0",
|
||||||
@ -32,18 +33,13 @@
|
|||||||
"hast-util-to-string": "^3.0.0",
|
"hast-util-to-string": "^3.0.0",
|
||||||
"hastscript": "^8.0.0",
|
"hastscript": "^8.0.0",
|
||||||
"html-escaper": "^3.0.3",
|
"html-escaper": "^3.0.3",
|
||||||
|
"markdown-it": "^14.1.0",
|
||||||
|
"markdown-it-prism": "^3.0.1",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
|
"prismjs": "^1.30.0",
|
||||||
"quicklink": "^3.0.1",
|
"quicklink": "^3.0.1",
|
||||||
"rehype-autolink-headings": "^7.0.0",
|
|
||||||
"rehype-slug": "^6.0.0",
|
|
||||||
"rehype-stringify": "^10.0.1",
|
|
||||||
"remark-parse": "^11.0.0",
|
|
||||||
"remark-prism": "^1.3.6",
|
|
||||||
"remark-rehype": "^11.1.2",
|
|
||||||
"remark-toc": "^9.0.0",
|
|
||||||
"sharp": "^0.32.6",
|
"sharp": "^0.32.6",
|
||||||
"solid-js": "^1.7.12",
|
"solid-js": "^1.7.12"
|
||||||
"unified": "^11.0.5"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0no-co/graphqlsp": "^1.15.1",
|
"@0no-co/graphqlsp": "^1.15.1",
|
||||||
|
|||||||
12
src/components/AboutMeContent.astro
Normal file
12
src/components/AboutMeContent.astro
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
import { getLanguageFromURL } from "@i18n/utils";
|
||||||
|
import { getAboutMe } from "../queries/global";
|
||||||
|
import { markdownToHTML } from "../utils/markdown";
|
||||||
|
import ArticleContent from "@components/ArticleContent.astro";
|
||||||
|
|
||||||
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
|
const data = await getAboutMe(pageLang);
|
||||||
|
const aboutMeHtmlContent = await markdownToHTML(data?.about || "");
|
||||||
|
---
|
||||||
|
|
||||||
|
<ArticleContent set:html={aboutMeHtmlContent} />
|
||||||
@ -1,12 +1,10 @@
|
|||||||
---
|
---
|
||||||
export interface Props {
|
import { getSiteSettings } from '@/src/queries';
|
||||||
counter: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { counter } = Astro.props as Props
|
const siteSettings = await getSiteSettings();
|
||||||
---
|
---
|
||||||
|
|
||||||
<script type="text/javascript" define:vars={{ counter }} is:inline>
|
{siteSettings ? <script type="text/javascript" define:vars={{ counter: siteSettings.ym_counter }} is:inline>
|
||||||
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||||
var z = null;m[i].l=1*new Date();
|
var z = null;m[i].l=1*new Date();
|
||||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||||
@ -16,6 +14,6 @@ const { counter } = Astro.props as Props
|
|||||||
ym(counter, "init", {
|
ym(counter, "init", {
|
||||||
clickmap: true,
|
clickmap: true,
|
||||||
trackLinks: true,
|
trackLinks: true,
|
||||||
accurateTrackBounce:true
|
accurateTrackBounce: true,
|
||||||
});
|
});
|
||||||
</script>
|
</script> : null}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const { url, id } = Astro.props as Props;
|
|||||||
<div id="disqus_thread"></div>
|
<div id="disqus_thread"></div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script define:vars={{ url, id }}>
|
<script is:inline define:vars={{ url, id }}>
|
||||||
var disqus_config = function () {
|
var disqus_config = function () {
|
||||||
this.page.url = url;
|
this.page.url = url;
|
||||||
this.page.identifier = id;
|
this.page.identifier = id;
|
||||||
@ -25,9 +25,9 @@ const { url, id } = Astro.props as Props;
|
|||||||
((function () {
|
((function () {
|
||||||
var e = document,
|
var e = document,
|
||||||
t = e.createElement("script");
|
t = e.createElement("script");
|
||||||
(t.src = "https://yarkov-tech.disqus.com/embed.js"),
|
((t.src = "https://yarkov-tech.disqus.com/embed.js"),
|
||||||
t.setAttribute("data-timestamp", +new Date()),
|
t.setAttribute("data-timestamp", +new Date()),
|
||||||
(e.head || e.body).appendChild(t);
|
(e.head || e.body).appendChild(t));
|
||||||
})(),
|
})(),
|
||||||
t.unobserve(e.target));
|
t.unobserve(e.target));
|
||||||
});
|
});
|
||||||
@ -39,9 +39,6 @@ const { url, id } = Astro.props as Props;
|
|||||||
"onthemetoggled",
|
"onthemetoggled",
|
||||||
() =>
|
() =>
|
||||||
document.readyState == "complete" &&
|
document.readyState == "complete" &&
|
||||||
setTimeout(
|
setTimeout(() => "DISQUS" in window && DISQUS.reset({ reload: true, config: disqus_config }), 100)
|
||||||
() => "DISQUS" in window && DISQUS.reset({ reload: true, config: disqus_config }),
|
|
||||||
100
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
---
|
---
|
||||||
import "@styles/global.scss";
|
import "@styles/global.scss";
|
||||||
import siteconfig from "siteconfig.json";
|
|
||||||
import { SITE_METADATA } from "@root/constants";
|
import { SITE_METADATA } from "@root/constants";
|
||||||
import YaMetrika from "./Analytics/YaMetrika.astro";
|
import YaMetrika from "./Analytics/YaMetrika.astro";
|
||||||
import { useTranslation } from "@root/i18n/utils";
|
import { useTranslation } from "@root/i18n/utils";
|
||||||
import { getSiteSettings } from "../queries";
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
title?: string;
|
title?: string;
|
||||||
@ -14,60 +12,59 @@ export interface Props {
|
|||||||
|
|
||||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||||
const { title, description } = Astro.props;
|
const { title, description } = Astro.props;
|
||||||
const siteSettings = await getSiteSettings();
|
|
||||||
const isDev = import.meta.env.DEV;
|
const isDev = import.meta.env.DEV;
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
---
|
---
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<!-- Fonts -->
|
<!-- Fonts -->
|
||||||
<link rel='preconnect' href='https://fonts.googleapis.com' />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
href='https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono&display=swap'
|
href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono&display=swap"
|
||||||
rel='preload'
|
rel="preload"
|
||||||
as='style'
|
as="style"
|
||||||
onload="this.rel='stylesheet'"
|
onload={`this.rel='stylesheet'`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Canonical URL -->
|
<!-- Canonical URL -->
|
||||||
<link rel='canonical' href={canonicalURL.href} />
|
<link rel="canonical" href={canonicalURL.href} />
|
||||||
|
|
||||||
<!-- Base metadata -->
|
<!-- Base metadata -->
|
||||||
<meta charset='utf-8' />
|
<meta charset="utf-8" />
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>{title || SITE_METADATA.title}</title>
|
<title>{title || SITE_METADATA.title}</title>
|
||||||
|
|
||||||
<!-- Metadata -->
|
<!-- Metadata -->
|
||||||
<meta name='title' content={title || SITE_METADATA.title} />
|
<meta name="title" content={title || SITE_METADATA.title} />
|
||||||
<meta name='generator' content={Astro.generator} />
|
<meta name="generator" content={Astro.generator} />
|
||||||
<meta name='description' content={description || t("site.description")} />
|
<meta name="description" content={description || t("site.description")} />
|
||||||
<meta name='theme-color' content={SITE_METADATA.themeColor} />
|
<meta name="theme-color" content={SITE_METADATA.themeColor} />
|
||||||
<meta name='msapplication-TileColor' content='#da532c' />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
|
|
||||||
<!-- Favicons -->
|
<!-- Favicons -->
|
||||||
<link rel='apple-touch-icon' sizes='180x180' href='/apple-touch-icon.png' />
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||||
<link
|
<link
|
||||||
rel='icon'
|
rel="icon"
|
||||||
type='image/png'
|
type="image/png"
|
||||||
sizes='32x32'
|
sizes="32x32"
|
||||||
href='/favicon-light-32x32.png'
|
href="/favicon-light-32x32.png"
|
||||||
media='(prefers-color-scheme: light)'
|
media="(prefers-color-scheme: light)"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel='icon'
|
rel="icon"
|
||||||
type='image/png'
|
type="image/png"
|
||||||
sizes='16x16'
|
sizes="16x16"
|
||||||
href='/favicon-light-16x16.png'
|
href="/favicon-light-16x16.png"
|
||||||
media='(prefers-color-scheme: light)'
|
media="(prefers-color-scheme: light)"
|
||||||
/>
|
/>
|
||||||
<link rel='icon' type='image/png' sizes='32x32' href='/favicon-dark-32x32.png' media='(prefers-color-scheme: dark)' />
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-dark-32x32.png" media="(prefers-color-scheme: dark)" />
|
||||||
<link rel='icon' type='image/png' sizes='16x16' href='/favicon-dark-16x16.png' media='(prefers-color-scheme: dark)' />
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-dark-16x16.png" media="(prefers-color-scheme: dark)" />
|
||||||
<link rel='manifest' href='/site.webmanifest' />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<link rel='mask-icon' href='/safari-pinned-tab.svg' color='#5bbad5' />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
<link rel='sitemap' href='/sitemap-index.xml' />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
<slot />
|
<slot />
|
||||||
<script is:inline src='/assets/js/detect-theme.js'></script>
|
<script is:inline src="/assets/js/detect-theme.js"></script>
|
||||||
<script src='../quicklink.js'></script>
|
<!-- <script src="../quicklink.js"></script> -->
|
||||||
{!isDev && siteSettings?.ym_counter && <YaMetrika counter={siteSettings.ym_counter} />}
|
{!isDev && <YaMetrika />}
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@ -1,24 +1,21 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from "astro-icon/components";
|
|
||||||
import { IS_SITE_UNDER_CONSTRUCTION, SITE_METADATA } from "@root/constants";
|
import { IS_SITE_UNDER_CONSTRUCTION, SITE_METADATA } from "@root/constants";
|
||||||
import ThemeSwitcher from "@components/ThemeSwticher";
|
import ThemeSwitcher from "@components/ThemeSwticher";
|
||||||
import LanguageSelect from "@components/LanguageSelect.astro";
|
import LanguageSelect from "@components/LanguageSelect.astro";
|
||||||
import NavToggle from "@components/NavToggle";
|
import NavToggle from "@components/NavToggle";
|
||||||
import UIString from "@components/UIString.astro";
|
import UIString from "@components/UIString.astro";
|
||||||
import TranslatedLink from "@components/TranslatedLink.astro";
|
import TranslatedLink from "@components/TranslatedLink.astro";
|
||||||
import { getContacts } from "../queries";
|
import HeaderContacts from "./HeaderContacts.astro";
|
||||||
|
|
||||||
const contacts = await getContacts();
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<header {...IS_SITE_UNDER_CONSTRUCTION ? { style: "top: 45px;" } : {}}>
|
<header {...IS_SITE_UNDER_CONSTRUCTION ? { style: "top: 45px;" } : {}}>
|
||||||
<div class='container'>
|
<div class="container">
|
||||||
<div class='logo'>
|
<div class="logo">
|
||||||
<TranslatedLink to='/'>
|
<TranslatedLink to="/">
|
||||||
{SITE_METADATA.name}
|
{SITE_METADATA.name}
|
||||||
</TranslatedLink>
|
</TranslatedLink>
|
||||||
</div>
|
</div>
|
||||||
<nav itemscope itemtype='http://schema.org/SiteNavigationElement'>
|
<nav itemscope itemtype="http://schema.org/SiteNavigationElement">
|
||||||
<ul>
|
<ul>
|
||||||
{
|
{
|
||||||
SITE_METADATA.nav.map((link) => (
|
SITE_METADATA.nav.map((link) => (
|
||||||
@ -26,32 +23,16 @@ const contacts = await getContacts();
|
|||||||
<TranslatedLink
|
<TranslatedLink
|
||||||
itemprop={"url"}
|
itemprop={"url"}
|
||||||
to={link.url}
|
to={link.url}
|
||||||
class:list={[{ active: Astro.url.pathname.includes(link.url) }]}>
|
class:list={[{ active: Astro.url.pathname.includes(link.url) }]}
|
||||||
|
>
|
||||||
<UIString key={link.key} />
|
<UIString key={link.key} />
|
||||||
</TranslatedLink>
|
</TranslatedLink>
|
||||||
</li>
|
</li>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
{
|
<HeaderContacts />
|
||||||
contacts && (
|
<div class="flex settings">
|
||||||
<div class='contacts'>
|
|
||||||
<a target='_blank' href={"mailto:" + contacts.email}>
|
|
||||||
Email
|
|
||||||
<Icon name='email' width='18' height='18' />
|
|
||||||
</a>
|
|
||||||
<a target='_blank' href={contacts.github}>
|
|
||||||
Github
|
|
||||||
<Icon name='github_filled' width='18' height='18' />
|
|
||||||
</a>
|
|
||||||
<a target='_blank' href={contacts.telegram}>
|
|
||||||
Telegram
|
|
||||||
<Icon name='telegram' width='18' height='18' />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div class='flex settings'>
|
|
||||||
<LanguageSelect />
|
<LanguageSelect />
|
||||||
<ThemeSwitcher client:visible />
|
<ThemeSwitcher client:visible />
|
||||||
</div>
|
</div>
|
||||||
@ -60,7 +41,7 @@ const contacts = await getContacts();
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<style lang='scss'>
|
<style lang="scss">
|
||||||
header {
|
header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -106,19 +87,6 @@ const contacts = await getContacts();
|
|||||||
text-transform: lowercase;
|
text-transform: lowercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& .contacts {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
column-gap: 10px;
|
|
||||||
margin-right: 30px;
|
|
||||||
& a {
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 0;
|
|
||||||
&:hover {
|
|
||||||
opacity: var(--opacity-hover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
& .settings {
|
& .settings {
|
||||||
column-gap: 13.5px;
|
column-gap: 13.5px;
|
||||||
}
|
}
|
||||||
@ -164,17 +132,6 @@ const contacts = await getContacts();
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& .contacts {
|
|
||||||
margin-right: 0;
|
|
||||||
margin-top: 2.5rem;
|
|
||||||
& a {
|
|
||||||
margin-right: 0.2rem;
|
|
||||||
}
|
|
||||||
& svg {
|
|
||||||
width: 21px;
|
|
||||||
height: 21px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
src/components/HeaderContacts.astro
Normal file
54
src/components/HeaderContacts.astro
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
import { Icon } from "astro-icon/components";
|
||||||
|
import { getContacts } from "../queries";
|
||||||
|
|
||||||
|
const contacts = await getContacts();
|
||||||
|
---
|
||||||
|
|
||||||
|
{
|
||||||
|
contacts && (
|
||||||
|
<div class="contacts">
|
||||||
|
<a target="_blank" href={"mailto:" + contacts.email}>
|
||||||
|
Email
|
||||||
|
<Icon name="email" width="18" height="18" />
|
||||||
|
</a>
|
||||||
|
<a target="_blank" href={contacts.github}>
|
||||||
|
Github
|
||||||
|
<Icon name="github_filled" width="18" height="18" />
|
||||||
|
</a>
|
||||||
|
<a target="_blank" href={contacts.telegram}>
|
||||||
|
Telegram
|
||||||
|
<Icon name="telegram" width="18" height="18" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.contacts {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 10px;
|
||||||
|
margin-right: 30px;
|
||||||
|
& a {
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0;
|
||||||
|
&:hover {
|
||||||
|
opacity: var(--opacity-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.contacts {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-top: 2.5rem;
|
||||||
|
& a {
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
}
|
||||||
|
& svg {
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -19,7 +19,7 @@ const lang = getLanguageFromURL(Astro.url.pathname);
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script define:vars={{ lang, DEFAULT_LANGUAGE, URL_BLOG_PREFIX }}>
|
<script is:inline define:vars={{ lang, DEFAULT_LANGUAGE, URL_BLOG_PREFIX }}>
|
||||||
const $select = document.getElementById("languageSelect");
|
const $select = document.getElementById("languageSelect");
|
||||||
|
|
||||||
const changeLanguageHandler = (e) => {
|
const changeLanguageHandler = (e) => {
|
||||||
@ -37,8 +37,7 @@ const lang = getLanguageFromURL(Astro.url.pathname);
|
|||||||
|
|
||||||
/* Move user to articles page with new language */
|
/* Move user to articles page with new language */
|
||||||
if (window.location.pathname.includes(`/${URL_BLOG_PREFIX}/`)) {
|
if (window.location.pathname.includes(`/${URL_BLOG_PREFIX}/`)) {
|
||||||
window.location.pathname =
|
window.location.pathname = `${newLang === DEFAULT_LANGUAGE ? "" : newLang + "/"}` + "articles/";
|
||||||
`${newLang === DEFAULT_LANGUAGE ? "" : newLang + "/"}` + "articles/";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
import { Icon } from "astro-icon/components";
|
|
||||||
import type { CollectionEntry } from "astro:content";
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
data: CollectionEntry<"skills">["data"];
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: { title, list },
|
|
||||||
} = Astro.props as Props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="skills-block">
|
|
||||||
<div>
|
|
||||||
<h3>{title}</h3>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
list.map((entry) => (
|
|
||||||
<li>
|
|
||||||
{entry.icon && <Icon name={entry.icon} width="20" height="20" />}
|
|
||||||
{entry.title}
|
|
||||||
</li>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.skills-block {
|
|
||||||
& h3 {
|
|
||||||
font-size: var(--font-size-md);
|
|
||||||
color: var(--font-color-grayer);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
& ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
& li {
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
column-gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
|
||||||
.skills-block {
|
|
||||||
& h3 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
& ul {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
<div class="skills-list"><slot /></div>
|
|
||||||
<style lang="scss">
|
|
||||||
.skills-list {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
||||||
column-gap: 40px;
|
|
||||||
row-gap: 30px;
|
|
||||||
margin-top: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
|
||||||
.skills-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
---
|
---
|
||||||
import type { MarkdownHeading } from "astro";
|
|
||||||
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants";
|
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants";
|
||||||
import BaseHead from "@components/BaseHead.astro";
|
import BaseHead from "@components/BaseHead.astro";
|
||||||
import BaseLayout from "@components/BaseLayout.astro";
|
import BaseLayout from "@components/BaseLayout.astro";
|
||||||
|
|||||||
@ -2,79 +2,42 @@ import type { APIContext } from "astro";
|
|||||||
// import { getCollection } from 'astro:content';
|
// import { getCollection } from 'astro:content';
|
||||||
|
|
||||||
export async function GET(context: APIContext) {
|
export async function GET(context: APIContext) {
|
||||||
return new Response("sitemap", { status: 200 });
|
const site = "https://yarkov.tech";
|
||||||
|
const urls: Array<{ loc: string; changefreq?: string; priority?: number }> = [];
|
||||||
|
|
||||||
|
urls.push(
|
||||||
|
{ loc: `${site}/`, changefreq: "weekly", priority: 1.0 },
|
||||||
|
{ loc: `${site}/articles/`, changefreq: "weekly", priority: 0.9 },
|
||||||
|
{ loc: `${site}/projects/`, changefreq: "monthly", priority: 0.8 },
|
||||||
|
{ loc: `${site}/about/`, changefreq: "monthly", priority: 0.7 }
|
||||||
|
);
|
||||||
|
|
||||||
|
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
${urls
|
||||||
|
.map(
|
||||||
|
(url) => `<url>
|
||||||
|
<loc>${url.loc}</loc>${
|
||||||
|
url.changefreq
|
||||||
|
? `
|
||||||
|
<changefreq>${url.changefreq}</changefreq>`
|
||||||
|
: ""
|
||||||
|
}${
|
||||||
|
url.priority !== undefined
|
||||||
|
? `
|
||||||
|
<priority>${url.priority}</priority>`
|
||||||
|
: ""
|
||||||
}
|
}
|
||||||
|
</url>`
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
</urlset>`;
|
||||||
|
|
||||||
// export async function GET(context: APIContext) {
|
return new Response(sitemap, {
|
||||||
// const site = context.site?.toString() || 'https://yourdomain.com';
|
status: 200,
|
||||||
|
headers: {
|
||||||
// // Get all collections
|
"Content-Type": "application/xml",
|
||||||
// const posts = await getCollection('blog');
|
"Cache-Control": "public, max-age=3600",
|
||||||
// const projects = await getCollection('projects');
|
},
|
||||||
|
});
|
||||||
// // Generate URLs for all content
|
}
|
||||||
// const urls: Array<{ loc: string; changefreq?: string; priority?: number }> = [];
|
|
||||||
|
|
||||||
// // Static pages
|
|
||||||
// urls.push(
|
|
||||||
// { loc: `${site}/`, changefreq: 'weekly', priority: 1.0 },
|
|
||||||
// { loc: `${site}/blog/`, changefreq: 'weekly', priority: 0.9 },
|
|
||||||
// { loc: `${site}/projects/`, changefreq: 'monthly', priority: 0.8 },
|
|
||||||
// { loc: `${site}/about/`, changefreq: 'monthly', priority: 0.7 },
|
|
||||||
// );
|
|
||||||
|
|
||||||
// // Blog posts
|
|
||||||
// posts
|
|
||||||
// .filter(post => !post.data.draft)
|
|
||||||
// .forEach(post => {
|
|
||||||
// urls.push({
|
|
||||||
// loc: `${site}/blog/${post.slug}/`,
|
|
||||||
// changefreq: 'monthly',
|
|
||||||
// priority: 0.8
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Projects
|
|
||||||
// projects
|
|
||||||
// .filter(project => !project.data.draft)
|
|
||||||
// .forEach(project => {
|
|
||||||
// urls.push({
|
|
||||||
// loc: `${site}/projects/${project.slug}/`,
|
|
||||||
// changefreq: 'monthly',
|
|
||||||
// priority: 0.7
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Get all unique tags
|
|
||||||
// const allTags = new Set<string>();
|
|
||||||
// posts.forEach(post => {
|
|
||||||
// post.data.tags?.forEach(tag => allTags.add(tag));
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Add tag pages
|
|
||||||
// allTags.forEach(tag => {
|
|
||||||
// urls.push({
|
|
||||||
// loc: `${site}/tags/${encodeURIComponent(tag)}/`,
|
|
||||||
// changefreq: 'weekly',
|
|
||||||
// priority: 0.5
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Generate sitemap XML
|
|
||||||
// const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
// <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
||||||
// ${urls.map(url => ` <url>
|
|
||||||
// <loc>${url.loc}</loc>${url.changefreq ? `
|
|
||||||
// <changefreq>${url.changefreq}</changefreq>` : ''}${url.priority !== undefined ? `
|
|
||||||
// <priority>${url.priority}</priority>` : ''}
|
|
||||||
// </url>`).join('\n')}
|
|
||||||
// </urlset>`;
|
|
||||||
|
|
||||||
// return new Response(sitemap, {
|
|
||||||
// status: 200,
|
|
||||||
// headers: {
|
|
||||||
// 'Content-Type': 'application/xml',
|
|
||||||
// 'Cache-Control': 'public, max-age=3600' // Cache for 1 hour
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|||||||
@ -2,23 +2,15 @@
|
|||||||
import { getLanguageFromURL, useTranslation } from "@i18n/utils";
|
import { getLanguageFromURL, useTranslation } from "@i18n/utils";
|
||||||
import PageLayout from "@layouts/default.astro";
|
import PageLayout from "@layouts/default.astro";
|
||||||
import PageHeading from "@components/PageHeading.astro";
|
import PageHeading from "@components/PageHeading.astro";
|
||||||
import ArticleContent from "@components/ArticleContent.astro";
|
import AboutMeContent from "@components/AboutMeContent.astro";
|
||||||
import { getAboutMe } from "../queries/global";
|
|
||||||
import { markdownToHTML } from "../utils/markdown";
|
|
||||||
|
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const data = await getAboutMe(pageLang);
|
|
||||||
if (!data?.about) return Astro.redirect("/404", 404);
|
|
||||||
const aboutMeHtmlContent = await markdownToHTML(data.about);
|
|
||||||
const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout noIndex lang={pageLang} title={t("pageTitle.about")}>
|
<PageLayout noIndex lang={pageLang} title={t("pageTitle.about")}>
|
||||||
<PageHeading heading={t("pageTitle.about")} />
|
<PageHeading heading={t("pageTitle.about")} />
|
||||||
<section>
|
<section>
|
||||||
<ArticleContent set:html={aboutMeHtmlContent} />
|
<AboutMeContent />
|
||||||
<!-- <SkillsList>
|
|
||||||
{skills.map((block) => <SkillsBlock data={block.data} />)}
|
|
||||||
</SkillsList> -->
|
|
||||||
</section>
|
</section>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { MarkdownHeading } from "astro";
|
import type { MarkdownHeading } from "astro";
|
||||||
import { DEFAULT_LANGUAGE } from "@root/constants";
|
import { DEFAULT_LANGUAGE } from "@root/constants";
|
||||||
import { ArticleQuery, RecentArticleQuery } from "../graphql/graphql";
|
import { ArticleQuery } from "../graphql/graphql";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there is a translation of an article in the current language of the page,
|
* If there is a translation of an article in the current language of the page,
|
||||||
|
|||||||
@ -1,65 +1,64 @@
|
|||||||
import { unified } from "unified";
|
// import { escape } from "html-escaper";
|
||||||
import remarkParse from "remark-parse";
|
// import { h } from "hastscript";
|
||||||
import remarkPrism from "remark-prism";
|
// import { toString } from "hast-util-to-string";
|
||||||
import remarkToc from "remark-toc";
|
import markdownit from "markdown-it";
|
||||||
import remarkRehype from "remark-rehype";
|
import prism from "markdown-it-prism";
|
||||||
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
|
||||||
import rehypeSlug from "rehype-slug";
|
|
||||||
import rehypeStringify from "rehype-stringify";
|
|
||||||
import { escape } from "html-escaper";
|
|
||||||
import { h } from "hastscript";
|
|
||||||
import { toString } from "hast-util-to-string";
|
|
||||||
// import { selectAll } from "hast-util-select";
|
// import { selectAll } from "hast-util-select";
|
||||||
|
|
||||||
|
const md = markdownit({
|
||||||
|
html: true,
|
||||||
|
}).use(prism);
|
||||||
|
|
||||||
|
// const createAnchorLabel = (heading: string) => {
|
||||||
|
// const node = h("span.anchor-label", escape(heading));
|
||||||
|
// if (node.properties) node.properties["is:raw"] = true;
|
||||||
|
// return node;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const processor = unified()
|
||||||
|
// .use(remarkParse)
|
||||||
|
// .use(remarkToc, { heading: "(table[ -]of[ -])?contents?|toc|содержание", maxDepth: 2 })
|
||||||
|
// .use(remarkPrism as any)
|
||||||
|
// .use(remarkRehype, { allowDangerousHtml: true })
|
||||||
|
// .use(rehypeSlug)
|
||||||
|
// .use(rehypeAutolinkHeadings, {
|
||||||
|
// properties: {
|
||||||
|
// class: "anchor-link",
|
||||||
|
// },
|
||||||
|
// behavior: "after",
|
||||||
|
// group: ({ tagName }) =>
|
||||||
|
// h(`div.heading-wrapper.level-${tagName}`, {
|
||||||
|
// tabIndex: -1,
|
||||||
|
// }),
|
||||||
|
// content: (heading) => [
|
||||||
|
// h(
|
||||||
|
// `span.anchor-icon`,
|
||||||
|
// undefined,
|
||||||
|
// h(
|
||||||
|
// "svg",
|
||||||
|
// {
|
||||||
|
// width: 18,
|
||||||
|
// height: 18,
|
||||||
|
// version: 1.1,
|
||||||
|
// viewBox: "0 0 16 16",
|
||||||
|
// xlmns: "http://www.w3.org/2000/svg",
|
||||||
|
// ariaHidden: "true",
|
||||||
|
// "data-filter": "none",
|
||||||
|
// },
|
||||||
|
// h("path", {
|
||||||
|
// fillRule: "evenodd",
|
||||||
|
// fill: "currentcolor",
|
||||||
|
// d: "M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z",
|
||||||
|
// })
|
||||||
|
// )
|
||||||
|
// ),
|
||||||
|
// createAnchorLabel(toString(heading)),
|
||||||
|
// ],
|
||||||
|
// });
|
||||||
|
|
||||||
export async function markdownToHTML(content: string): Promise<string> {
|
export async function markdownToHTML(content: string): Promise<string> {
|
||||||
const createAnchorLabel = (heading: string) => {
|
const start = performance.now();
|
||||||
const node = h("span.anchor-label", escape(heading));
|
const html = md.render(content);
|
||||||
if (node.properties) node.properties["is:raw"] = true;
|
console.log(performance.now() - start);
|
||||||
return node;
|
return html;
|
||||||
};
|
|
||||||
|
|
||||||
const file = await unified()
|
|
||||||
.use(remarkParse)
|
|
||||||
.use(remarkToc, { heading: "(table[ -]of[ -])?contents?|toc|содержание", maxDepth: 2 })
|
|
||||||
.use(remarkPrism as any)
|
|
||||||
.use(remarkRehype, { allowDangerousHtml: true })
|
|
||||||
.use(rehypeSlug)
|
|
||||||
.use(rehypeAutolinkHeadings, {
|
|
||||||
properties: {
|
|
||||||
class: "anchor-link",
|
|
||||||
},
|
|
||||||
behavior: "after",
|
|
||||||
group: ({ tagName }) =>
|
|
||||||
h(`div.heading-wrapper.level-${tagName}`, {
|
|
||||||
tabIndex: -1,
|
|
||||||
}),
|
|
||||||
content: (heading) => [
|
|
||||||
h(
|
|
||||||
`span.anchor-icon`,
|
|
||||||
undefined,
|
|
||||||
h(
|
|
||||||
"svg",
|
|
||||||
{
|
|
||||||
width: 18,
|
|
||||||
height: 18,
|
|
||||||
version: 1.1,
|
|
||||||
viewBox: "0 0 16 16",
|
|
||||||
xlmns: "http://www.w3.org/2000/svg",
|
|
||||||
ariaHidden: "true",
|
|
||||||
"data-filter": "none",
|
|
||||||
},
|
|
||||||
h("path", {
|
|
||||||
fillRule: "evenodd",
|
|
||||||
fill: "currentcolor",
|
|
||||||
d: "M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
),
|
|
||||||
createAnchorLabel(toString(heading)),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.use(rehypeStringify, { allowDangerousHtml: true })
|
|
||||||
.process(content);
|
|
||||||
|
|
||||||
return String(file);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user