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",
|
||||
server: {
|
||||
port: 9966,
|
||||
host: true,
|
||||
allowedHosts: ["showcase.yarkov.tech"],
|
||||
},
|
||||
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",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev --host",
|
||||
"build": "astro check && astro build",
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"start": "node --env-file=.env ./dist/server/entry.mjs",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
@ -25,6 +25,7 @@
|
||||
"@astrojs/rss": "^4.0.13",
|
||||
"@astrojs/solid-js": "^5.1.3",
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"astro": "^5.15.8",
|
||||
"astro-icon": "^1.1.5",
|
||||
"graphql": "^16.12.0",
|
||||
@ -32,18 +33,13 @@
|
||||
"hast-util-to-string": "^3.0.0",
|
||||
"hastscript": "^8.0.0",
|
||||
"html-escaper": "^3.0.3",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-prism": "^3.0.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"prismjs": "^1.30.0",
|
||||
"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",
|
||||
"solid-js": "^1.7.12",
|
||||
"unified": "^11.0.5"
|
||||
"solid-js": "^1.7.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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 {
|
||||
counter: number;
|
||||
}
|
||||
import { getSiteSettings } from '@/src/queries';
|
||||
|
||||
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)};
|
||||
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; }}
|
||||
@ -14,8 +12,8 @@ const { counter } = Astro.props as Props
|
||||
(window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym");
|
||||
|
||||
ym(counter, "init", {
|
||||
clickmap:true,
|
||||
trackLinks:true,
|
||||
accurateTrackBounce:true
|
||||
clickmap: true,
|
||||
trackLinks: true,
|
||||
accurateTrackBounce: true,
|
||||
});
|
||||
</script>
|
||||
</script> : null}
|
||||
|
||||
@ -11,7 +11,7 @@ const { url, id } = Astro.props as Props;
|
||||
<div id="disqus_thread"></div>
|
||||
</footer>
|
||||
|
||||
<script define:vars={{ url, id }}>
|
||||
<script is:inline define:vars={{ url, id }}>
|
||||
var disqus_config = function () {
|
||||
this.page.url = url;
|
||||
this.page.identifier = id;
|
||||
@ -25,9 +25,9 @@ const { url, id } = Astro.props as Props;
|
||||
((function () {
|
||||
var e = document,
|
||||
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()),
|
||||
(e.head || e.body).appendChild(t);
|
||||
(e.head || e.body).appendChild(t));
|
||||
})(),
|
||||
t.unobserve(e.target));
|
||||
});
|
||||
@ -39,9 +39,6 @@ const { url, id } = Astro.props as Props;
|
||||
"onthemetoggled",
|
||||
() =>
|
||||
document.readyState == "complete" &&
|
||||
setTimeout(
|
||||
() => "DISQUS" in window && DISQUS.reset({ reload: true, config: disqus_config }),
|
||||
100
|
||||
)
|
||||
setTimeout(() => "DISQUS" in window && DISQUS.reset({ reload: true, config: disqus_config }), 100)
|
||||
);
|
||||
</script>
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
---
|
||||
import "@styles/global.scss";
|
||||
import siteconfig from "siteconfig.json";
|
||||
import { SITE_METADATA } from "@root/constants";
|
||||
import YaMetrika from "./Analytics/YaMetrika.astro";
|
||||
import { useTranslation } from "@root/i18n/utils";
|
||||
import { getSiteSettings } from "../queries";
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
@ -14,60 +12,59 @@ export interface Props {
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
const { title, description } = Astro.props;
|
||||
const siteSettings = await getSiteSettings();
|
||||
const isDev = import.meta.env.DEV;
|
||||
const t = useTranslation(Astro);
|
||||
---
|
||||
|
||||
<head>
|
||||
<!-- Fonts -->
|
||||
<link rel='preconnect' href='https://fonts.googleapis.com' />
|
||||
<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<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'
|
||||
rel='preload'
|
||||
as='style'
|
||||
onload="this.rel='stylesheet'"
|
||||
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"
|
||||
as="style"
|
||||
onload={`this.rel='stylesheet'`}
|
||||
/>
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel='canonical' href={canonicalURL.href} />
|
||||
<link rel="canonical" href={canonicalURL.href} />
|
||||
|
||||
<!-- Base metadata -->
|
||||
<meta charset='utf-8' />
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1' />
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title || SITE_METADATA.title}</title>
|
||||
|
||||
<!-- Metadata -->
|
||||
<meta name='title' content={title || SITE_METADATA.title} />
|
||||
<meta name='generator' content={Astro.generator} />
|
||||
<meta name='description' content={description || t("site.description")} />
|
||||
<meta name='theme-color' content={SITE_METADATA.themeColor} />
|
||||
<meta name='msapplication-TileColor' content='#da532c' />
|
||||
<meta name="title" content={title || SITE_METADATA.title} />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="description" content={description || t("site.description")} />
|
||||
<meta name="theme-color" content={SITE_METADATA.themeColor} />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
|
||||
<!-- 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
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='32x32'
|
||||
href='/favicon-light-32x32.png'
|
||||
media='(prefers-color-scheme: light)'
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/favicon-light-32x32.png"
|
||||
media="(prefers-color-scheme: light)"
|
||||
/>
|
||||
<link
|
||||
rel='icon'
|
||||
type='image/png'
|
||||
sizes='16x16'
|
||||
href='/favicon-light-16x16.png'
|
||||
media='(prefers-color-scheme: light)'
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/favicon-light-16x16.png"
|
||||
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='16x16' href='/favicon-dark-16x16.png' media='(prefers-color-scheme: dark)' />
|
||||
<link rel='manifest' href='/site.webmanifest' />
|
||||
<link rel='mask-icon' href='/safari-pinned-tab.svg' color='#5bbad5' />
|
||||
<link rel='sitemap' href='/sitemap-index.xml' />
|
||||
<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="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<slot />
|
||||
<script is:inline src='/assets/js/detect-theme.js'></script>
|
||||
<script src='../quicklink.js'></script>
|
||||
{!isDev && siteSettings?.ym_counter && <YaMetrika counter={siteSettings.ym_counter} />}
|
||||
<script is:inline src="/assets/js/detect-theme.js"></script>
|
||||
<!-- <script src="../quicklink.js"></script> -->
|
||||
{!isDev && <YaMetrika />}
|
||||
</head>
|
||||
|
||||
@ -1,24 +1,21 @@
|
||||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { IS_SITE_UNDER_CONSTRUCTION, SITE_METADATA } from "@root/constants";
|
||||
import ThemeSwitcher from "@components/ThemeSwticher";
|
||||
import LanguageSelect from "@components/LanguageSelect.astro";
|
||||
import NavToggle from "@components/NavToggle";
|
||||
import UIString from "@components/UIString.astro";
|
||||
import TranslatedLink from "@components/TranslatedLink.astro";
|
||||
import { getContacts } from "../queries";
|
||||
|
||||
const contacts = await getContacts();
|
||||
import HeaderContacts from "./HeaderContacts.astro";
|
||||
---
|
||||
|
||||
<header {...IS_SITE_UNDER_CONSTRUCTION ? { style: "top: 45px;" } : {}}>
|
||||
<div class='container'>
|
||||
<div class='logo'>
|
||||
<TranslatedLink to='/'>
|
||||
<div class="container">
|
||||
<div class="logo">
|
||||
<TranslatedLink to="/">
|
||||
{SITE_METADATA.name}
|
||||
</TranslatedLink>
|
||||
</div>
|
||||
<nav itemscope itemtype='http://schema.org/SiteNavigationElement'>
|
||||
<nav itemscope itemtype="http://schema.org/SiteNavigationElement">
|
||||
<ul>
|
||||
{
|
||||
SITE_METADATA.nav.map((link) => (
|
||||
@ -26,32 +23,16 @@ const contacts = await getContacts();
|
||||
<TranslatedLink
|
||||
itemprop={"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} />
|
||||
</TranslatedLink>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
{
|
||||
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>
|
||||
)
|
||||
}
|
||||
<div class='flex settings'>
|
||||
<HeaderContacts />
|
||||
<div class="flex settings">
|
||||
<LanguageSelect />
|
||||
<ThemeSwitcher client:visible />
|
||||
</div>
|
||||
@ -60,7 +41,7 @@ const contacts = await getContacts();
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style lang='scss'>
|
||||
<style lang="scss">
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@ -106,19 +87,6 @@ const contacts = await getContacts();
|
||||
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 {
|
||||
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>
|
||||
</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 changeLanguageHandler = (e) => {
|
||||
@ -37,8 +37,7 @@ const lang = getLanguageFromURL(Astro.url.pathname);
|
||||
|
||||
/* Move user to articles page with new language */
|
||||
if (window.location.pathname.includes(`/${URL_BLOG_PREFIX}/`)) {
|
||||
window.location.pathname =
|
||||
`${newLang === DEFAULT_LANGUAGE ? "" : newLang + "/"}` + "articles/";
|
||||
window.location.pathname = `${newLang === DEFAULT_LANGUAGE ? "" : newLang + "/"}` + "articles/";
|
||||
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 BaseHead from "@components/BaseHead.astro";
|
||||
import BaseLayout from "@components/BaseLayout.astro";
|
||||
|
||||
@ -2,79 +2,42 @@ import type { APIContext } from "astro";
|
||||
// import { getCollection } from 'astro:content';
|
||||
|
||||
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>`;
|
||||
|
||||
return new Response(sitemap, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
"Cache-Control": "public, max-age=3600",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// export async function GET(context: APIContext) {
|
||||
// const site = context.site?.toString() || 'https://yourdomain.com';
|
||||
|
||||
// // Get all collections
|
||||
// const posts = await getCollection('blog');
|
||||
// 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 PageLayout from "@layouts/default.astro";
|
||||
import PageHeading from "@components/PageHeading.astro";
|
||||
import ArticleContent from "@components/ArticleContent.astro";
|
||||
import { getAboutMe } from "../queries/global";
|
||||
import { markdownToHTML } from "../utils/markdown";
|
||||
import AboutMeContent from "@components/AboutMeContent.astro";
|
||||
|
||||
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);
|
||||
---
|
||||
|
||||
<PageLayout noIndex lang={pageLang} title={t("pageTitle.about")}>
|
||||
<PageHeading heading={t("pageTitle.about")} />
|
||||
<section>
|
||||
<ArticleContent set:html={aboutMeHtmlContent} />
|
||||
<!-- <SkillsList>
|
||||
{skills.map((block) => <SkillsBlock data={block.data} />)}
|
||||
</SkillsList> -->
|
||||
<AboutMeContent />
|
||||
</section>
|
||||
</PageLayout>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { MarkdownHeading } from "astro";
|
||||
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,
|
||||
|
||||
@ -1,65 +1,64 @@
|
||||
import { unified } from "unified";
|
||||
import remarkParse from "remark-parse";
|
||||
import remarkPrism from "remark-prism";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkRehype from "remark-rehype";
|
||||
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 { escape } from "html-escaper";
|
||||
// import { h } from "hastscript";
|
||||
// import { toString } from "hast-util-to-string";
|
||||
import markdownit from "markdown-it";
|
||||
import prism from "markdown-it-prism";
|
||||
// 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> {
|
||||
const createAnchorLabel = (heading: string) => {
|
||||
const node = h("span.anchor-label", escape(heading));
|
||||
if (node.properties) node.properties["is:raw"] = true;
|
||||
return node;
|
||||
};
|
||||
|
||||
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);
|
||||
const start = performance.now();
|
||||
const html = md.render(content);
|
||||
console.log(performance.now() - start);
|
||||
return html;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user