mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
refactor 0_0
This commit is contained in:
parent
001e8eba08
commit
943a074058
@ -3,7 +3,6 @@ import { defineConfig } from "astro/config";
|
||||
/**
|
||||
* Integrations
|
||||
*/
|
||||
// import mdx from "@astrojs/mdx";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import solidJs from "@astrojs/solid-js";
|
||||
import image from "@astrojs/image";
|
||||
@ -65,28 +64,31 @@ export default defineConfig({
|
||||
* Articles
|
||||
*/
|
||||
{
|
||||
name: "posts",
|
||||
name: "articles",
|
||||
label: "Публикации",
|
||||
label_singular: "публикацию",
|
||||
description:
|
||||
"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой",
|
||||
folder: "src/content/blog",
|
||||
slug: "{{year}}-{{month}}-{{day}}_{{slug}}",
|
||||
slug: "{{slug}}",
|
||||
create: true,
|
||||
delete: true,
|
||||
i18n: true,
|
||||
fields: [
|
||||
{
|
||||
name: "coverImage",
|
||||
widget: "image",
|
||||
label: "Постер",
|
||||
required: false,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "draft",
|
||||
label: "В черновике",
|
||||
widget: "boolean",
|
||||
required: true,
|
||||
default: true,
|
||||
hint: "Опубликована ли статья",
|
||||
i18n: true,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
name: "author",
|
||||
@ -95,19 +97,19 @@ export default defineConfig({
|
||||
search_fields: ["firstName", "lastName"],
|
||||
collection: "authors",
|
||||
label: "Автор",
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "title",
|
||||
widget: "string",
|
||||
label: "Заголовок",
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
widget: "string",
|
||||
label: "Описание",
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "tags",
|
||||
@ -117,25 +119,27 @@ export default defineConfig({
|
||||
multiple: true,
|
||||
value_field: "title",
|
||||
search_fields: ["title"],
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "pubDate",
|
||||
widget: "datetime",
|
||||
label: "Дата публикации",
|
||||
required: true,
|
||||
picker_utc: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "updatedDate",
|
||||
widget: "datetime",
|
||||
label: "Дата обновления",
|
||||
required: true,
|
||||
picker_utc: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "body",
|
||||
widget: "markdown",
|
||||
label: "Содержание",
|
||||
i18n: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -171,24 +175,28 @@ export default defineConfig({
|
||||
folder: "src/content/authors",
|
||||
create: true,
|
||||
delete: true,
|
||||
i18n: true,
|
||||
fields: [
|
||||
{
|
||||
label: "Имя",
|
||||
name: "firstName",
|
||||
widget: "string",
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
label: "Фамилия",
|
||||
name: "lastName",
|
||||
widget: "string",
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
label: "Об авторе",
|
||||
name: "body",
|
||||
widget: "markdown",
|
||||
required: false,
|
||||
i18n: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -199,6 +207,7 @@ export default defineConfig({
|
||||
name: "projects",
|
||||
label: "Проекты",
|
||||
label_singular: "проект",
|
||||
description: "Основные проекты, которые были разработаны в выложены в открытый доступ",
|
||||
folder: "src/content/projects",
|
||||
create: true,
|
||||
delete: true,
|
||||
@ -211,18 +220,9 @@ export default defineConfig({
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
{
|
||||
name: "draft",
|
||||
label: "В черновике",
|
||||
widget: "boolean",
|
||||
required: true,
|
||||
i18n: true,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
name: "title",
|
||||
label: "Название",
|
||||
default: "my-project",
|
||||
widget: "string",
|
||||
required: true,
|
||||
i18n: true,
|
||||
@ -231,7 +231,6 @@ export default defineConfig({
|
||||
name: "year",
|
||||
label: "В каком году разработал",
|
||||
widget: "number",
|
||||
default: 2023,
|
||||
required: true,
|
||||
i18n: true,
|
||||
},
|
||||
@ -308,9 +307,9 @@ export default defineConfig({
|
||||
label: "Настройки",
|
||||
files: [
|
||||
{
|
||||
name: "site-settings",
|
||||
name: "siteconfig",
|
||||
label: "Настройки веб-сайта",
|
||||
file: "src/content/site-settings.md",
|
||||
file: "siteconfig.json",
|
||||
fields: [
|
||||
{
|
||||
label: "Глобальный заголовок",
|
||||
@ -320,8 +319,9 @@ export default defineConfig({
|
||||
},
|
||||
{
|
||||
label: "В разработке",
|
||||
name: "isUnderConstruction",
|
||||
name: "isSiteUnderConstruction",
|
||||
widget: "boolean",
|
||||
required: false,
|
||||
default: false,
|
||||
hint: "Отображает баннер на всех старницах о том, что веб-сайт находится в разработке",
|
||||
},
|
||||
|
||||
1178
package-lock.json
generated
1178
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -19,11 +19,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/image": "^0.14.1",
|
||||
"@astrojs/mdx": "^0.16.0",
|
||||
"@astrojs/rss": "^2.1.0",
|
||||
"@astrojs/sitemap": "^1.0.1",
|
||||
"@astrojs/solid-js": "^2.0.2",
|
||||
"@netlify/functions": "^1.4.0",
|
||||
"@sindresorhus/slugify": "^2.2.0",
|
||||
"astro": "^2.0.10",
|
||||
"astro-icon": "^0.8.0",
|
||||
"astro-netlify-cms": "^0.5.3",
|
||||
|
||||
7
siteconfig.json
Normal file
7
siteconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"globalTitle": "Sergey Yarkov",
|
||||
"articles": {
|
||||
"recentLimit": 5
|
||||
},
|
||||
"isSiteUnderConstruction": false
|
||||
}
|
||||
@ -38,6 +38,10 @@ const { url, id } = Astro.props as Props;
|
||||
window.addEventListener(
|
||||
"onthemetoggled",
|
||||
() =>
|
||||
document.readyState == "complete" && setTimeout(() => DISQUS.reset({ reload: true, config: disqus_config }), 100)
|
||||
document.readyState == "complete" &&
|
||||
setTimeout(
|
||||
() => "DISQUS" in window && DISQUS.reset({ reload: true, config: disqus_config }),
|
||||
100
|
||||
)
|
||||
);
|
||||
</script>
|
||||
|
||||
225
src/components/ArticleContent.astro
Normal file
225
src/components/ArticleContent.astro
Normal file
@ -0,0 +1,225 @@
|
||||
<div itemprop="articleBody" class="article-content"><slot /></div>
|
||||
|
||||
<style lang="scss" is:global>
|
||||
.article-content {
|
||||
& h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
& p,
|
||||
em,
|
||||
q {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& p {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
& ul,
|
||||
ol {
|
||||
padding-left: 1.5rem;
|
||||
line-height: var(--line-height-base);
|
||||
margin: 1.5rem 0;
|
||||
& li {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: var(--line-height-base);
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
& q {
|
||||
font-style: italic;
|
||||
quotes: "« " " »";
|
||||
}
|
||||
|
||||
& a {
|
||||
color: var(--font-color);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--color-blue-500);
|
||||
text-underline-offset: 2.2px;
|
||||
text-decoration-thickness: 2px;
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: var(--color-blue-500);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
& figure {
|
||||
margin: 0 0 1em;
|
||||
& figcaption {
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
}
|
||||
|
||||
& hr {
|
||||
background-color: var(--border-color);
|
||||
margin: 40px 0;
|
||||
border: none;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
& pre {
|
||||
font-size: var(--font-size-sm);
|
||||
border-radius: var(--border-radius-md);
|
||||
}
|
||||
|
||||
& :not(pre) > code {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
background: var(--background-inline-code);
|
||||
border-radius: var(--border-radius-lg);
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
padding: 0.2rem 0.3rem;
|
||||
}
|
||||
|
||||
& figcaption {
|
||||
font-style: italic;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
& .table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
& table {
|
||||
display: table;
|
||||
border-radius: var(--border-radius-md);
|
||||
min-width: 100%;
|
||||
font-size: var(--font-size-md);
|
||||
border: 1px solid var(--border-color);
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin: 5px 0;
|
||||
& thead th {
|
||||
background-color: var(--background-card);
|
||||
border-top-left-radius: var(--border-radius-md);
|
||||
border-top-right-radius: var(--border-radius-md);
|
||||
}
|
||||
& td,
|
||||
th {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
& tbody tr:last-child td {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
& li {
|
||||
list-style: disc;
|
||||
& li {
|
||||
list-style-type: circle;
|
||||
}
|
||||
&::marker {
|
||||
color: var(--color-blue-500);
|
||||
font-size: 1.3rem;
|
||||
line-height: var(--line-height-none);
|
||||
}
|
||||
}
|
||||
|
||||
& blockquote {
|
||||
margin: 1.5rem 0;
|
||||
font-style: italic;
|
||||
border-left: 0.25rem solid var(--border-color);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
& .heading-wrapper {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
margin: 3rem 0 1rem 0;
|
||||
padding-bottom: 0.35rem;
|
||||
|
||||
& h2 {
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
& h3 {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
& h4 {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .heading-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
margin-inline-start: 1px;
|
||||
& .anchor-link {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
text-decoration: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
& .anchor-icon {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: -13px;
|
||||
}
|
||||
& svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
& .anchor-label {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
& .anchor-link {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
& .languages-available {
|
||||
margin: 25px 0;
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.article-content {
|
||||
& .heading-wrapper {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
& .anchor-link {
|
||||
& .anchor-icon {
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
import "./ArticleContent.scss";
|
||||
---
|
||||
|
||||
<div itemprop="articleBody" class="article-content"><slot /></div>
|
||||
@ -1,221 +0,0 @@
|
||||
.article-content {
|
||||
& h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
& p,
|
||||
em,
|
||||
q {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& p {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
& ul,
|
||||
ol {
|
||||
padding-left: 1.5rem;
|
||||
line-height: var(--line-height-base);
|
||||
margin: 1.5rem 0;
|
||||
& li {
|
||||
font-size: var(--font-size-lg);
|
||||
line-height: var(--line-height-base);
|
||||
margin-bottom: 0.5rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
& q {
|
||||
font-style: italic;
|
||||
quotes: "« " " »";
|
||||
}
|
||||
|
||||
& a {
|
||||
color: var(--font-color);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--color-blue-500);
|
||||
text-underline-offset: 2.2px;
|
||||
text-decoration-thickness: 2px;
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: var(--color-blue-500);
|
||||
text-decoration-thickness: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
& figure {
|
||||
margin: 0 0 1em;
|
||||
& figcaption {
|
||||
color: var(--color-gray-400);
|
||||
}
|
||||
}
|
||||
|
||||
& hr {
|
||||
background-color: var(--border-color);
|
||||
margin: 40px 0;
|
||||
border: none;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
& pre {
|
||||
font-size: var(--font-size-sm);
|
||||
border-radius: var(--border-radius-md);
|
||||
}
|
||||
|
||||
& :not(pre) > code {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
background: var(--background-inline-code);
|
||||
border-radius: var(--border-radius-lg);
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
font-family: "IBM Plex Mono", monospace;
|
||||
padding: 0.2rem 0.3rem;
|
||||
}
|
||||
|
||||
& figcaption {
|
||||
font-style: italic;
|
||||
font-size: 1em;
|
||||
text-align: center;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
& .table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
& table {
|
||||
display: table;
|
||||
border-radius: var(--border-radius-md);
|
||||
min-width: 100%;
|
||||
font-size: var(--font-size-md);
|
||||
border: 1px solid var(--border-color);
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin: 5px 0;
|
||||
& thead th {
|
||||
background-color: var(--background-card);
|
||||
border-top-left-radius: var(--border-radius-md);
|
||||
border-top-right-radius: var(--border-radius-md);
|
||||
}
|
||||
& td,
|
||||
th {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
& tbody tr:last-child td {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
& li {
|
||||
list-style: disc;
|
||||
& li {
|
||||
list-style-type: circle;
|
||||
}
|
||||
&::marker {
|
||||
color: var(--color-blue-500);
|
||||
font-size: 1.3rem;
|
||||
line-height: var(--line-height-none);
|
||||
}
|
||||
}
|
||||
|
||||
& blockquote {
|
||||
margin: 1.5rem 0;
|
||||
font-style: italic;
|
||||
border-left: 0.25rem solid var(--border-color);
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
& .heading-wrapper {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
margin: 3rem 0 1rem 0;
|
||||
padding-bottom: 0.35rem;
|
||||
|
||||
& h2 {
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
& h3 {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
& h4 {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .heading-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
margin-inline-start: 1px;
|
||||
& .anchor-link {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
text-decoration: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
& .anchor-icon {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: -13px;
|
||||
}
|
||||
& svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
& .anchor-label {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:hover {
|
||||
& .anchor-link {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
& .languages-available {
|
||||
margin: 25px 0;
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.article-content {
|
||||
& .heading-wrapper {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
& .anchor-link {
|
||||
& .anchor-icon {
|
||||
left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/components/ArticleCover.astro
Normal file
34
src/components/ArticleCover.astro
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
import { Picture } from "@astrojs/image/components";
|
||||
|
||||
export interface Props {
|
||||
src: string;
|
||||
alt?: string;
|
||||
}
|
||||
|
||||
const { src, alt } = Astro.props as Props;
|
||||
---
|
||||
|
||||
<div class="cover">
|
||||
<figure>
|
||||
<Picture
|
||||
itemprop="image"
|
||||
src={src}
|
||||
sizes="(max-width: 450px) 450px, 700px"
|
||||
widths={[450, 700]}
|
||||
formats={["webp"]}
|
||||
aspectRatio={"16:9"}
|
||||
alt={alt ? alt : ""}
|
||||
background={"chocolate"}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.cover {
|
||||
margin: 20px 0;
|
||||
& figure {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,29 +1,44 @@
|
||||
---
|
||||
import { getLanguageFromURL } from "@i18n/utils";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { URL_BLOG_PREFIX } from "@root/pages/[...blog].astro";
|
||||
import { DEFAULT_LANGUAGE } from "@root/constants";
|
||||
import { getLanguageFromURL } from "@i18n/utils";
|
||||
import { createDateFormatter } from "@root/utils";
|
||||
|
||||
export interface Props {
|
||||
href: string;
|
||||
title: string;
|
||||
date: Date;
|
||||
entry: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||
// const isNonDefaultLanguage = DEFAULT_LANGUAGE !== lang;
|
||||
|
||||
const { href, title, date } = Astro.props as Props;
|
||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||
const {
|
||||
entry: {
|
||||
id,
|
||||
data: { title, pubDate },
|
||||
},
|
||||
} = Astro.props as Props;
|
||||
const slug = slugify(title, { lowercase: true });
|
||||
const articleLang = id.split("/")[0];
|
||||
---
|
||||
|
||||
<article itemprop="blogPosts" itemscope itemtype="http://schema.org/BlogPosting" class="article-item">
|
||||
<a href={href}>
|
||||
<article
|
||||
itemprop="blogPosts"
|
||||
itemscope
|
||||
itemtype="http://schema.org/BlogPosting"
|
||||
class="article-item"
|
||||
>
|
||||
<a
|
||||
href={articleLang === DEFAULT_LANGUAGE
|
||||
? `/${URL_BLOG_PREFIX}/${pubDate.toISOString().split("T")[0]}/${slug}`
|
||||
: `/${articleLang}/${URL_BLOG_PREFIX}/${pubDate.toISOString().split("T")[0]}/${slug}`}
|
||||
>
|
||||
<div class="flex">
|
||||
<h3 itemprop="headline">{title}</h3>
|
||||
<!-- {isNonDefaultLanguage && <sup>{lang.toLocaleUpperCase()}</sup>} -->
|
||||
{articleLang !== pageLang && <sup>{articleLang.toLocaleUpperCase()}</sup>}
|
||||
</div>
|
||||
<p>
|
||||
<time datetime={date.toString()}>
|
||||
{createDateFormatter(lang).format(date)}
|
||||
<time datetime={pubDate.toISOString()}>
|
||||
{createDateFormatter(pageLang).format(pubDate)}
|
||||
</time>
|
||||
</p>
|
||||
</a>
|
||||
|
||||
@ -1,23 +1,24 @@
|
||||
---
|
||||
import { Picture } from "@astrojs/image/components";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { Icon } from "astro-icon";
|
||||
import { createDateFormatter } from "@root/utils";
|
||||
// import { isURL } from "@utils/string";
|
||||
import ArticleCover from "@components/ArticleCover.astro";
|
||||
import ArticleComments from "@components/ArticleComments.astro";
|
||||
// import TranslatedLink from "../TranslatedLink";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
// import { useTranslation } from "@root/i18n/utils";
|
||||
import "./ArticlePost.scss";
|
||||
// import { useTranslation } from "@i18n/utils";
|
||||
|
||||
export type Props = {
|
||||
article: CollectionEntry<"blog">;
|
||||
lang: string;
|
||||
};
|
||||
|
||||
const { article, lang } = Astro.props as Props;
|
||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||
const {
|
||||
data: { coverImage, title, author, description, tags, pubDate },
|
||||
slug,
|
||||
} = article;
|
||||
|
||||
// const t = useTranslation(Astro);
|
||||
---
|
||||
|
||||
@ -53,24 +54,7 @@ const {
|
||||
>
|
||||
<span itemprop="author">{author}</span>
|
||||
</div>
|
||||
{
|
||||
coverImage && (
|
||||
<div class="cover">
|
||||
<figure>
|
||||
<Picture
|
||||
itemprop="image"
|
||||
src={coverImage}
|
||||
sizes="(max-width: 450px) 450px, 700px"
|
||||
widths={[450, 700]}
|
||||
formats={["webp"]}
|
||||
aspectRatio={"16:9"}
|
||||
alt={title}
|
||||
background={"chocolate"}
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{coverImage && <ArticleCover src={coverImage} alt={title} />}
|
||||
<div class="description">
|
||||
<p>
|
||||
{description}
|
||||
@ -81,8 +65,9 @@ const {
|
||||
<ArticleComments url={canonicalUrl.href} id={slug} />
|
||||
</article>
|
||||
|
||||
<script define:vars={{ slug }}>
|
||||
<script define:vars={{ slug, IS_DEV: import.meta.env.DEV }}>
|
||||
(async () => {
|
||||
if (IS_DEV) return;
|
||||
const response = await fetch(`/api/views?slug=${slug}`, { method: "POST" });
|
||||
if (!response.ok) return;
|
||||
const views = await response.text();
|
||||
@ -91,3 +76,98 @@ const {
|
||||
if ($views) $views.textContent = views;
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style lang="scss" is:global>
|
||||
.article {
|
||||
& header {
|
||||
& div:nth-child(1) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
& .views {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& span {
|
||||
margin-left: 3.5px;
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--font-color-grayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
& .heading {
|
||||
margin-top: 1.125rem;
|
||||
|
||||
& h1 {
|
||||
font-weight: 600;
|
||||
font-size: 2.125rem;
|
||||
}
|
||||
|
||||
& p {
|
||||
font-size: 1.125rem;
|
||||
line-height: var(--line-height-base);
|
||||
}
|
||||
}
|
||||
|
||||
& .tags {
|
||||
font-size: var(--font-size-sm);
|
||||
text-transform: uppercase;
|
||||
& ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 7px;
|
||||
row-gap: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
& .published {
|
||||
margin-bottom: 1.25em;
|
||||
color: var(--font-color-gray);
|
||||
}
|
||||
}
|
||||
|
||||
& .cover {
|
||||
margin: 20px 0;
|
||||
& figure {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .description p {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& footer {
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
& img {
|
||||
border-radius: var(--border-radius-lg);
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
&:not([src*=".svg"]) {
|
||||
filter: var(--image-filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.article {
|
||||
& header {
|
||||
& div:nth-child(1) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
& .tags {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
& .heading h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,92 +0,0 @@
|
||||
.article {
|
||||
& header {
|
||||
& div:nth-child(1) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
& .views {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
& span {
|
||||
margin-left: 3.5px;
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--font-color-grayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
& .heading {
|
||||
margin-top: 1.125rem;
|
||||
|
||||
& h1 {
|
||||
font-weight: 600;
|
||||
font-size: 2.125rem;
|
||||
}
|
||||
|
||||
& p {
|
||||
font-size: 1.125rem;
|
||||
line-height: var(--line-height-base);
|
||||
}
|
||||
}
|
||||
|
||||
& .tags {
|
||||
font-size: var(--font-size-sm);
|
||||
text-transform: uppercase;
|
||||
& ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 7px;
|
||||
row-gap: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
& .published {
|
||||
margin-bottom: 1.25em;
|
||||
color: var(--font-color-gray);
|
||||
}
|
||||
}
|
||||
|
||||
& .cover {
|
||||
margin: 20px 0;
|
||||
& figure {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& .description p {
|
||||
line-height: var(--line-height-base);
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
& footer {
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
& img {
|
||||
border-radius: var(--border-radius-lg);
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
&:not([src*=".svg"]) {
|
||||
filter: var(--image-filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 560px) {
|
||||
.article {
|
||||
& header {
|
||||
& div:nth-child(1) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
& .tags {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
& .heading h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,6 @@ export interface Props {
|
||||
}
|
||||
|
||||
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
|
||||
@ -2,11 +2,12 @@
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import ArticleItem from "@components/ArticleItem.astro";
|
||||
import EmptyList from "@components/EmptyList.astro";
|
||||
import siteconfig from "siteconfig.json";
|
||||
export interface Props {
|
||||
articles: CollectionEntry<"blog">[];
|
||||
length?: number | undefined;
|
||||
}
|
||||
const { articles, length = 5 } = Astro.props as Props;
|
||||
const { articles, length = siteconfig.articles.recentLimit } = Astro.props as Props;
|
||||
const isEmpty = articles.length === 0;
|
||||
---
|
||||
|
||||
@ -19,11 +20,7 @@ const isEmpty = articles.length === 0;
|
||||
<div itemscope itemtype="http://schema.org/Blog">
|
||||
{
|
||||
!isEmpty ? (
|
||||
articles
|
||||
.slice(0, length)
|
||||
.map((article) => (
|
||||
<ArticleItem href={`/articles/${article.slug}`} title={article.data.title} date={article.data.pubDate} />
|
||||
))
|
||||
articles.slice(0, length).map((article) => <ArticleItem entry={article} />)
|
||||
) : (
|
||||
<EmptyList />
|
||||
)
|
||||
|
||||
69
src/components/SkillsBlock.astro
Normal file
69
src/components/SkillsBlock.astro
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
import { Icon } from "astro-icon";
|
||||
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>
|
||||
20
src/components/SkillsList.astro
Normal file
20
src/components/SkillsList.astro
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
---
|
||||
|
||||
<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,13 +1,10 @@
|
||||
import siteconfig from "../siteconfig.json";
|
||||
|
||||
export const DEFAULT_LANGUAGE: LanguageKeys = "ru";
|
||||
|
||||
/* Show banner about website development */
|
||||
export const IS_SITE_UNDER_CONSTRUCTION =
|
||||
import.meta.env.IS_SITE_UNDER_CONSTRUCTION === "true" ? true : false;
|
||||
|
||||
export const IS_SITE_UNDER_CONSTRUCTION = siteconfig.isSiteUnderConstruction;
|
||||
export const SUPPORTED_LANGUAGES: SupportedLanguages = ["ru", "en"];
|
||||
|
||||
export const SITE_METADATA: SiteMetadataType = {
|
||||
title: "Sergey Yarkov",
|
||||
title: siteconfig.globalTitle,
|
||||
name: "yarkov.tech",
|
||||
author: "Sergey Yarkov",
|
||||
themeColor: "#1e2327",
|
||||
|
||||
24
src/content/authors/en/сергей-ярков.md
Normal file
24
src/content/authors/en/сергей-ярков.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
firstName: Sergey
|
||||
lastName: Yarkov
|
||||
---
|
||||
My name is Sergey and I am a web developer. My main activity is creating web sites. I am interested in areas such as programming, embedded systems, and electronics.
|
||||
I started making web sites in 2017, starting with simple HTML pages, and in 2019 I started learning programming. At present, I have experience in **full-stack** web development of client-server applications that solve a wide range of tasks.
|
||||
Some of my personal [projects](/projects/) I put in public access on [Github](https://github.com/sergeyyarkov).
|
||||
|
||||
## Purpose of this site
|
||||
|
||||
I originally created this website for myself, to consolidate my knowledge by writing articles.
|
||||
Since 2022, I have been posting materials on this website, and now at any time I can return to any topic and learn it again.
|
||||
My articles can also help someone with mastering a particular topic. The main direction of this site is IT,
|
||||
and [here](/articles/) I publish materials on topics that interest me, as well as talk about my [projects](/projects/).
|
||||
I am absolutely calm about any criticism and I do not claim to be the best programmer in the world,
|
||||
so do not judge the materials of this resource harshly.
|
||||
|
||||
## Skills
|
||||
|
||||
This list shows the skills and technologies that I have used/use in my projects.
|
||||
For creating web projects, I use **Node.js** and the **Adonis.js** framework for the server-side.
|
||||
For the client-side, I develop using **React.js** or **Alpine.js**.
|
||||
|
||||
<q>this website is made using the [**Astro.js**](https://astro.build) framework and [**Solid.js**](https://www.solidjs.com/) library.</q>
|
||||
28
src/content/authors/ru/сергей-ярков.md
Normal file
28
src/content/authors/ru/сергей-ярков.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
firstName: Сергей
|
||||
lastName: Ярков
|
||||
---
|
||||
Мое имя **Сергей** я являюсь веб-разработчиком. Основной деятельностью моей работы является создание веб-сайтов.
|
||||
Мне интересны такие сферы как программирование, встраиваемые системы и электроника.
|
||||
Веб-сайты я начал делать с 2017 года начиная с верстки простых HTML страничек и с 2019 года я начал
|
||||
обучаться программированием и на сегодняшний момент я имею опыт в **full-stack** веб-разработке клиент-серверных приложений,
|
||||
которые решают самые разные задачи.
|
||||
Некоторые из своих личных [проектов](/projects/) я выкладываю в общий доступ на [Github](https://github.com/sergeyyarkov).
|
||||
|
||||
## Зачем этот сайт
|
||||
|
||||
Первоначально я создал этот сайт для себя, чтобы закреплять свои знания путем написания статей. С 2022 года я начал
|
||||
выкладывать материалы на этот сайт и сейчас в любой момент я могу вернуться к какой-либо теме и еще раз её усвоить.
|
||||
Мои статьи также смогут кому-то помочь с освоением какой-либо темы.
|
||||
Главным направлением этого сайта является IT, и [здесь](/articles/) я публикую материалы на темы, которые меня интересуют,
|
||||
а также рассказываю о своих [проектах](/projects/).
|
||||
Я отношусь абсолютно спокойно к любой критике и я не претендую на звание лучшего программиста в мире, поэтому
|
||||
не судите строго материалы этого ресурса.
|
||||
|
||||
## Навыки
|
||||
|
||||
Этот список показывает имеющиеся у меня навыки и технологии которые я когда-либо использовал/использую в своих проектах.
|
||||
Для создания веб-проектов в качестве серверной части я использую **Node.js** и фреймворк **Adonis.js**.
|
||||
Клиентскую часть я разрабатываю на **React.js** или **Alpine.js**.
|
||||
|
||||
<q>этот веб-сайт сделан с помощью фреймворка **[Astro.js](https://astro.build)** и библиотеки **[Solid.js](https://www.solidjs.com/)**</q>
|
||||
@ -1,4 +0,0 @@
|
||||
---
|
||||
firstName: Sergey
|
||||
lastName: Yarkov
|
||||
---
|
||||
@ -0,0 +1,17 @@
|
||||
---
|
||||
coverImage: /media/serial-port.jpg
|
||||
draft: false
|
||||
author: Sergey Yarkov
|
||||
title: Using the WebSerial API to communicate with the microcontroller
|
||||
description: We will talk about what is WebSerial API, how it can be used in a
|
||||
browser to read and write data to a serial device and where it can be applied.
|
||||
tags:
|
||||
- JavaScript
|
||||
- AVR
|
||||
- WebSerial API
|
||||
- Tutorial
|
||||
pubDate: 2022-11-04T21:00:00.000Z
|
||||
updatedDate: 2023-02-13T16:34:23.326Z
|
||||
---
|
||||
|
||||
# ...
|
||||
@ -0,0 +1,16 @@
|
||||
---
|
||||
coverImage: null
|
||||
draft: false
|
||||
author: Sergey Yarkov
|
||||
title: 74HC595 shift register control by microcontroller
|
||||
description: This tutorial shows how to control the shift register of the
|
||||
74HC595 in conjunction with the AVR microcontroller.
|
||||
tags:
|
||||
- AVR
|
||||
- Digital Electronics
|
||||
- Tutorial
|
||||
pubDate: 2023-02-14T19:23:55.622Z
|
||||
updatedDate: 2023-02-14T19:23:55.646Z
|
||||
---
|
||||
|
||||
...
|
||||
@ -0,0 +1,15 @@
|
||||
---
|
||||
draft: false
|
||||
author: Sergey Yarkov
|
||||
title: Управление сдвиговым регистром 74HC595 микрокотнороллером
|
||||
description: В этом туториале рассматривается то, как управлять сдвиговым
|
||||
регистром 74HC595 в связке с микроконтроллером AVR.
|
||||
tags:
|
||||
- AVR
|
||||
- Digital Electronics
|
||||
- Tutorial
|
||||
pubDate: 2023-02-14T19:23:55.622Z
|
||||
updatedDate: 2023-02-14T19:23:55.646Z
|
||||
---
|
||||
|
||||
...
|
||||
@ -3,7 +3,8 @@ import { defineCollection, z } from "astro:content";
|
||||
const blog = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
coverImage: z.string().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
coverImage: z.string().nullable().optional(),
|
||||
author: z.string(),
|
||||
tags: z.string().array().optional(),
|
||||
description: z.string(),
|
||||
@ -16,7 +17,6 @@ const blog = defineCollection({
|
||||
.or(z.date())
|
||||
.optional()
|
||||
.transform((str) => (str ? new Date(str) : undefined)),
|
||||
draft: z.boolean(),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -42,7 +42,6 @@ const projects = defineCollection({
|
||||
sourceUrl: z.string(),
|
||||
demoUrl: z.string().nullable().optional(),
|
||||
articleUrl: z.string().nullable().optional(),
|
||||
draft: z.boolean(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: triangle_filled
|
||||
draft: false
|
||||
title: devthread
|
||||
year: 2021
|
||||
description: Static website for publishing various articles, developed with
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: round-shop-two
|
||||
draft: false
|
||||
title: digital-store
|
||||
year: 2019
|
||||
description: Customizable online store with control panel for selling digital goods
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: round-school
|
||||
draft: false
|
||||
title: educt
|
||||
year: 2022
|
||||
description: Learning Management System (LMS) to publish your courses, lectures, lessons
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: chip
|
||||
draft: false
|
||||
title: memory-led-game
|
||||
year: 2022
|
||||
description: Analogue of the popular game "Simon says" on the AVR microcontroller
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: nodejs
|
||||
draft: false
|
||||
title: node-docker-example
|
||||
year: 2022
|
||||
description: An example of using Docker to deploy a Node.js application
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: chat
|
||||
draft: false
|
||||
title: react-chat-app
|
||||
year: 2021
|
||||
description: Client-server web chat application developed with Node.js and Socket.io
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: serial
|
||||
draft: false
|
||||
title: web-serial-example
|
||||
year: 2022
|
||||
description: An example of using the Web Serial API to interact with the
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: book
|
||||
draft: false
|
||||
title: yarkov.tech
|
||||
year: 2023
|
||||
description: My personal website where you can find information about me and a
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: triangle_filled
|
||||
draft: false
|
||||
title: devthread
|
||||
year: 2021
|
||||
description: Статический веб-сайт для публикации различных статей, разработанный
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: round-shop-two
|
||||
draft: false
|
||||
title: digital-store
|
||||
year: 2019
|
||||
description: Настраиваемый интернет-магазин с панелью управления для продажи
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: round-school
|
||||
draft: false
|
||||
title: educt
|
||||
year: 2022
|
||||
description: Система управления обучением (LMS) для публикации ваших курсов, лекций, уроков
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: chip
|
||||
draft: false
|
||||
title: memory-led-game
|
||||
year: 2022
|
||||
description: Аналог популярной игры "Саймон говорит" на AVR микроконтроллере
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: nodejs
|
||||
draft: false
|
||||
title: node-docker-example
|
||||
year: 2022
|
||||
description: Пример использования Docker для развертывания Node.js приложения
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: chat
|
||||
draft: false
|
||||
title: react-chat-app
|
||||
year: 2021
|
||||
description: Клиент-серверное веб приложение чата разработанное при помощи
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: serial
|
||||
draft: false
|
||||
title: web-serial-example
|
||||
year: 2022
|
||||
description: Пример использования Web Serial API для взаимодействия с
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
---
|
||||
icon: book
|
||||
draft: false
|
||||
title: yarkov.tech
|
||||
year: 2023
|
||||
description: Мой персональный веб-сайт где находятся информация обо мне и список
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
---
|
||||
globalTitle: yarkov.tech
|
||||
articles:
|
||||
recentLimit: 5
|
||||
---
|
||||
@ -3,8 +3,8 @@ import type { CollectionEntry } from "astro:content";
|
||||
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants";
|
||||
import BaseHead from "@components/BaseHead.astro";
|
||||
import BaseLayout from "@components/BaseLayout.astro";
|
||||
import ArticleContent from "@components/ArticleContent/ArticleContent.astro";
|
||||
import ArticlePost from "@components/ArticlePost/ArticlePost.astro";
|
||||
import ArticleContent from "@components/ArticleContent.astro";
|
||||
import ArticlePost from "@components/ArticlePost.astro";
|
||||
import OpenGraph from "@components/OpenGraph.astro";
|
||||
|
||||
export interface Props {
|
||||
|
||||
36
src/pages/[...blog].astro
Normal file
36
src/pages/[...blog].astro
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
import { CollectionEntry, getCollection } from "astro:content";
|
||||
import ArticleLayout from "@root/layouts/article.astro";
|
||||
import { DEFAULT_LANGUAGE } from "@root/constants";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
|
||||
export const URL_BLOG_PREFIX = "blog";
|
||||
|
||||
/**
|
||||
* Generate articles pages from `blog` collection.
|
||||
* The final url will be `http://website.com/blog/2023-01-01/my-article` with default language
|
||||
* and `http://website.com/{language-code}/blog/2023-01-01/my-article` with other languages.
|
||||
*/
|
||||
export async function getStaticPaths() {
|
||||
const collection = await getCollection("blog", (e) => !e.data.draft);
|
||||
return collection.map((article) => {
|
||||
const [languageCode] = article.slug.split("/");
|
||||
const slug = slugify(article.data.title, { lowercase: true });
|
||||
const pubDate = article.data.pubDate.toISOString().split("T")[0];
|
||||
const url =
|
||||
DEFAULT_LANGUAGE !== languageCode
|
||||
? `${languageCode}/${URL_BLOG_PREFIX}/${pubDate}/${slug}`
|
||||
: `${URL_BLOG_PREFIX}/${pubDate}/${slug}`;
|
||||
|
||||
return { params: { blog: url }, props: { article } };
|
||||
});
|
||||
}
|
||||
|
||||
type Props = { article: CollectionEntry<"blog"> };
|
||||
|
||||
const { Content } = await Astro.props.article.render();
|
||||
---
|
||||
|
||||
<ArticleLayout article={Astro.props.article}>
|
||||
<Content />
|
||||
</ArticleLayout>
|
||||
@ -1,22 +0,0 @@
|
||||
---
|
||||
import { CollectionEntry, getCollection } from "astro:content";
|
||||
import ArticleLayout from "@root/layouts/article.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const articles = await getCollection("blog");
|
||||
return articles
|
||||
.filter((a) => !a.data.draft)
|
||||
.map((article) => ({
|
||||
params: { slug: `articles/${article.slug}` },
|
||||
props: { article },
|
||||
}));
|
||||
}
|
||||
|
||||
type Props = { article: CollectionEntry<"blog"> };
|
||||
|
||||
const { Content } = await Astro.props.article.render();
|
||||
---
|
||||
|
||||
<ArticleLayout article={Astro.props.article}>
|
||||
<Content />
|
||||
</ArticleLayout>
|
||||
@ -3,6 +3,7 @@ import { getLanguageKeys } from "@i18n/utils";
|
||||
import { DEFAULT_LANGUAGE } from "@root/constants";
|
||||
import About from "@templates/About.astro";
|
||||
|
||||
// TODO: do not generate pages without required data
|
||||
export function getStaticPaths() {
|
||||
return getLanguageKeys()
|
||||
.filter((lang) => lang !== DEFAULT_LANGUAGE)
|
||||
|
||||
@ -1,26 +1,28 @@
|
||||
---
|
||||
import PageLayout from "@layouts/default.astro";
|
||||
import { getCollection, getEntryBySlug } from "astro:content";
|
||||
import { getLanguageFromURL } from "@i18n/utils";
|
||||
// import { useTranslation } from "@i18n/utils";
|
||||
// import * as API from "@root/lib/api";
|
||||
import PageLayout from "@layouts/default.astro";
|
||||
import PageHeading from "@components/PageHeading.astro";
|
||||
// import SkillsList from "@components/SkillsList";
|
||||
// import SkillsBlock from "@components/SkillsBlock";
|
||||
// import ArticleContent from "@components/ArticleContent";
|
||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||
import SkillsList from "@components/SkillsList.astro";
|
||||
import SkillsBlock from "@components/SkillsBlock.astro";
|
||||
import ArticleContent from "@components/ArticleContent.astro";
|
||||
|
||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||
const skills = await getCollection("skills", (e) => e.id.startsWith(`${pageLang}/`));
|
||||
const about = await getEntryBySlug("authors", `${pageLang}/сергей-ярков`);
|
||||
const data = await about?.render();
|
||||
|
||||
// const t = useTranslation(Astro);
|
||||
// const data = await API.fetchAboutMe(Astro);
|
||||
// const { skills } = SITE_METADATA;
|
||||
---
|
||||
|
||||
<PageLayout noIndex lang={lang} title={"Обо мне"}>
|
||||
<PageLayout noIndex lang={pageLang} title={"Обо мне"}>
|
||||
<PageHeading heading={"Обо мне"} />
|
||||
<section>
|
||||
<!-- <ArticleContent>
|
||||
<ArticleContent>
|
||||
{data && <data.Content />}
|
||||
</ArticleContent> -->
|
||||
<!-- <SkillsList>
|
||||
{skills.map((block) => <SkillsBlock data={block} />)}
|
||||
</SkillsList> -->
|
||||
</ArticleContent>
|
||||
<SkillsList>
|
||||
{skills.map((block) => <SkillsBlock data={block.data} />)}
|
||||
</SkillsList>
|
||||
</section>
|
||||
</PageLayout>
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import PageLayout from "@layouts/default.astro";
|
||||
import Splash from "@components/Splash/Splash.astro";
|
||||
import SplashHeading from "@root/components/Splash/SplashHeading.astro";
|
||||
import SplashLinks from "@root/components/Splash/SplashLinks.astro";
|
||||
import SplashLink from "@root/components/Splash/SplashLink.astro";
|
||||
import { getCollection } from "astro:content";
|
||||
import RecentArticles from "@root/components/RecentArticles.astro";
|
||||
|
||||
const articles = (await getCollection("blog"))
|
||||
.filter((p) => !p.data.draft)
|
||||
.sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf());
|
||||
const articles = (await getCollection("blog", (e) => !e.data.draft)).sort(
|
||||
(a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf()
|
||||
);
|
||||
---
|
||||
|
||||
<PageLayout description="Home page" lang={"ru"}>
|
||||
|
||||
@ -8,6 +8,6 @@ const lang = getLanguageFromURL(Astro.url.pathname);
|
||||
<PageLayout lang={lang}>
|
||||
<div class="container">
|
||||
<h1 style="font-size:var(--font-size-5xl);">404</h1>
|
||||
<p>{"Страница не найдена"}</p>
|
||||
<p>{"Страницу которую вы пытаетесь найти не существует."}</p>
|
||||
</div>
|
||||
</PageLayout>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user