cms config

This commit is contained in:
Sergey Yarkov 2023-02-13 23:00:29 +03:00
parent 598afe2e32
commit 2dbf994ec6
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
51 changed files with 823 additions and 134 deletions

View File

@ -1,8 +1,8 @@
{ {
"printWidth": 120, "printWidth": 100,
"semi": true, "semi": true,
"singleQuote": false, "singleQuote": false,
"tabWidth": 2, "tabWidth": 2,
"trailingComma": "es5", "trailingComma": "es5",
"useTabs": true "useTabs": true
} }

View File

@ -22,7 +22,7 @@ import { h } from "hastscript";
import { toString } from "hast-util-to-string"; import { toString } from "hast-util-to-string";
import { escape } from "html-escaper"; import { escape } from "html-escaper";
import languages from "./src/i18n/languages"; import languages from "./src/i18n/languages";
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "./src/constants"; import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES, SITE_METADATA } from "./src/constants";
const createAnchorLabel = (heading) => { const createAnchorLabel = (heading) => {
const node = h("span.anchor-label", escape(heading)); const node = h("span.anchor-label", escape(heading));
@ -48,11 +48,11 @@ export default defineConfig({
preview: false, preview: false,
}, },
display_url: "https://yarkov.tech", display_url: "https://yarkov.tech",
// i18n: { i18n: {
// locales: SUPPORTED_LANGUAGES, locales: SUPPORTED_LANGUAGES,
// default_locale: DEFAULT_LANGUAGE, default_locale: DEFAULT_LANGUAGE,
// structure: "multiple_files", structure: "multiple_folders",
// }, },
locale: DEFAULT_LANGUAGE, locale: DEFAULT_LANGUAGE,
backend: { backend: {
name: "test-repo", name: "test-repo",
@ -61,6 +61,9 @@ export default defineConfig({
media_folder: "public/media", media_folder: "public/media",
public_folder: "/media", public_folder: "/media",
collections: [ collections: [
/**
* Articles
*/
{ {
name: "posts", name: "posts",
label: "Публикации", label: "Публикации",
@ -75,6 +78,14 @@ export default defineConfig({
widget: "image", widget: "image",
label: "Постер", label: "Постер",
}, },
{
name: "draft",
label: "В черновике",
widget: "boolean",
required: true,
default: true,
hint: "Опубликована ли статья",
},
{ {
name: "author", name: "author",
widget: "relation", widget: "relation",
@ -107,9 +118,17 @@ export default defineConfig({
}, },
{ {
name: "pubDate", name: "pubDate",
widget: "date", widget: "datetime",
label: "Дата публикации", label: "Дата публикации",
required: true, required: true,
picker_utc: true,
},
{
name: "updatedDate",
widget: "datetime",
label: "Дата обновления",
required: true,
picker_utc: true,
}, },
{ {
name: "body", name: "body",
@ -118,6 +137,9 @@ export default defineConfig({
}, },
], ],
}, },
/**
* Tags
*/
{ {
name: "tags", name: "tags",
label: "Тэги", label: "Тэги",
@ -135,6 +157,9 @@ export default defineConfig({
}, },
], ],
}, },
/**
* Authors
*/
{ {
name: "authors", name: "authors",
label: "Авторы", label: "Авторы",
@ -159,12 +184,88 @@ export default defineConfig({
}, },
{ {
label: "Об авторе", label: "Об авторе",
name: "about", name: "body",
widget: "markdown", widget: "markdown",
required: false, required: false,
}, },
], ],
}, },
/**
* Projects
*/
{
name: "projects",
label: "Проекты",
label_singular: "проект",
folder: "src/content/projects",
create: true,
delete: true,
i18n: true,
fields: [
{
name: "icon",
label: "Иконка",
widget: "string",
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,
},
{
name: "year",
label: "В каком году разработал",
widget: "number",
default: 2023,
required: true,
i18n: true,
},
{
name: "description",
label: "Описание",
widget: "string",
required: true,
i18n: true,
},
{
name: "sourceUrl",
label: "Ссылка на исходник",
widget: "string",
required: true,
i18n: true,
},
{
name: "demoUrl",
label: "Ссылка на демонстрацию",
widget: "string",
required: false,
i18n: true,
},
{
name: "articleUrl",
label: "Ссылка на статью",
widget: "string",
required: false,
i18n: true,
},
],
},
/**
* Skills
*/
{ {
name: "skills", name: "skills",
label: "Навыки", label: "Навыки",
@ -179,6 +280,42 @@ export default defineConfig({
}, },
], ],
}, },
/**
* Settings
*/
{
name: "settings",
label: "Настройки",
files: [
{
name: "site-settings",
label: "Настройки веб-сайта",
file: "src/content/site-settings.yml",
fields: [
{
label: "Глобальный заголовок",
name: "globalTitle",
widget: "string",
hint: "Отображется во вкладке браузера.",
},
{
label: "Статьи",
name: "articles",
widget: "object",
fields: [
{
name: "recentLimit",
label: "Лимит на отображение",
widget: "number",
hint: "Кол-во публикаций на гл.странице",
default: 5,
},
],
},
],
},
],
},
], ],
}, },
}), }),

14
package-lock.json generated
View File

@ -22,6 +22,7 @@
"hastscript": "^7.2.0", "hastscript": "^7.2.0",
"html-escaper": "^3.0.3", "html-escaper": "^3.0.3",
"ioredis": "^5.3.0", "ioredis": "^5.3.0",
"mime-types": "^2.1.35",
"quicklink": "^2.3.0", "quicklink": "^2.3.0",
"rehype-autolink-headings": "^6.1.1", "rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0", "rehype-slug": "^5.1.0",
@ -29,6 +30,7 @@
"solid-js": "^1.6.10" "solid-js": "^1.6.10"
}, },
"devDependencies": { "devDependencies": {
"@types/mime-types": "^2.1.1",
"sass": "^1.58.0" "sass": "^1.58.0"
} }
}, },
@ -1689,6 +1691,12 @@
"resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.3.tgz",
"integrity": "sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==" "integrity": "sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ=="
}, },
"node_modules/@types/mime-types": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==",
"dev": true
},
"node_modules/@types/ms": { "node_modules/@types/ms": {
"version": "0.7.31", "version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",
@ -14506,6 +14514,12 @@
"resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.3.tgz",
"integrity": "sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==" "integrity": "sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ=="
}, },
"@types/mime-types": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.1.tgz",
"integrity": "sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==",
"dev": true
},
"@types/ms": { "@types/ms": {
"version": "0.7.31", "version": "0.7.31",
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz",

View File

@ -31,6 +31,7 @@
"hastscript": "^7.2.0", "hastscript": "^7.2.0",
"html-escaper": "^3.0.3", "html-escaper": "^3.0.3",
"ioredis": "^5.3.0", "ioredis": "^5.3.0",
"mime-types": "^2.1.35",
"quicklink": "^2.3.0", "quicklink": "^2.3.0",
"rehype-autolink-headings": "^6.1.1", "rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.1.0", "rehype-slug": "^5.1.0",
@ -38,6 +39,7 @@
"solid-js": "^1.6.10" "solid-js": "^1.6.10"
}, },
"devDependencies": { "devDependencies": {
"@types/mime-types": "^2.1.1",
"sass": "^1.58.0" "sass": "^1.58.0"
} }
} }

View File

@ -15,7 +15,7 @@ export type Props = {
const { article, lang } = Astro.props as Props; const { article, lang } = Astro.props as Props;
const canonicalUrl = new URL(Astro.url.pathname, Astro.site); const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
const { const {
data: { coverImage, title, description, tags, pubDate }, data: { coverImage, title, author, description, tags, pubDate },
slug, slug,
} = article; } = article;
// const t = useTranslation(Astro); // const t = useTranslation(Astro);
@ -48,8 +48,10 @@ const {
<h1 itemprop="headline">{title}</h1> <h1 itemprop="headline">{title}</h1>
</div> </div>
<div class="published"> <div class="published">
<time itemprop="datePublished" datetime={pubDate.toString()}>{createDateFormatter(lang).format(pubDate)}</time> <time itemprop="datePublished" datetime={pubDate.toString()}
<span itemprop="author">{"Sergey Yarkov"}</span> >{createDateFormatter(lang).format(pubDate)}</time
>
<span itemprop="author">{author}</span>
</div> </div>
{ {
coverImage && ( coverImage && (

View File

@ -4,7 +4,7 @@ import { SITE_METADATA } from "@root/constants";
export interface Props { export interface Props {
title?: string; title?: string;
description: string; description?: string;
image?: string; image?: string;
} }
@ -35,7 +35,7 @@ const { title, description } = Astro.props;
<!-- 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} /> {description && <meta name="description" content={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" />
@ -55,25 +55,22 @@ const { title, description } = Astro.props;
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
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-dark-16x16.png" media="(prefers-color-scheme: dark)" /> 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="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" />
<!-- Open Graph / Facebook -->
<!-- <meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={new URL(image, Astro.url)} /> -->
<!-- Twitter -->
<!-- <meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={Astro.url} />
<meta property="twitter:title" content={title} />
<meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} /> -->
<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> -->

View File

@ -17,17 +17,17 @@ import NavToggle from "@components/NavToggle";
{ {
( (
<li> <li>
<HeaderLink itemprop={"url"} href={"#"}> <HeaderLink itemprop={"url"} href={"/articles"}>
статьи статьи
</HeaderLink> </HeaderLink>
</li> </li>
<li> <li>
<HeaderLink itemprop={"url"} href={"#"}> <HeaderLink itemprop={"url"} href={"/projects"}>
проекты проекты
</HeaderLink> </HeaderLink>
</li> </li>
<li> <li>
<HeaderLink itemprop={"url"} href={"#"}> <HeaderLink itemprop={"url"} href={"/about"}>
обо мне обо мне
</HeaderLink> </HeaderLink>
</li> </li>

View File

@ -0,0 +1,33 @@
---
import mime from 'mime-types'
import { extname } from 'node:path'
import { isURL } from "@utils/string";
export interface Props {
title: string;
siteName: string;
description: string;
type: string;
url: string;
image?: {
path: string;
width: string;
height: string;
type: string;
}
}
const {title, siteName, description, url, image} = Astro.props as Props;
---
<meta property="og:title" content={title} />
<meta property="og:site_name" content={siteName} />
<meta property="og:description" content={description} />
<meta property="og:type" content="article" />
<meta property="og:url" content={url} />
{image && (
<meta property="og:image" content={isURL(image.path) ? image.path : `${Astro.url.origin}${image.path}`} />
<meta property="og:image:width" content="700" />
<meta property="og:image:height" content="400" />
<meta property="og:image:type" content={`${mime.lookup(extname(image.path))}`} />
)}

View File

@ -0,0 +1,91 @@
---
import { DEFAULT_LANGUAGE } from "@root/constants";
import { getLanguageFromURL } from "@i18n/utils";
import { Icon } from "astro-icon";
import type { CollectionEntry } from "astro:content";
// import UIString from "../UIString";
export interface Props {
data: CollectionEntry<"projects">["data"];
}
const { data } = Astro.props as Props;
// const lang = getLanguageFromURL(Astro.url.pathname);
const { icon, title, year, description, articleUrl, sourceUrl, demoUrl } = data;
---
<div class="project-item">
<div class="project-item__heading">
<h2>
<Icon name={icon} width="20" height="20" />
{title}
</h2>
<span>{year}</span>
</div>
<div class="project-item__description">
<p>{description}</p>
</div>
<div class="project-item__links">
{
articleUrl && (
<a class="btn-link" href={articleUrl} target="_blank">
Статья
</a>
)
}
{
sourceUrl && (
<a class="btn-link" href={sourceUrl} target="_blank">
Исходник
<Icon name="link-out" width="15" height="15" />
</a>
)
}
{
demoUrl && (
<a class="btn-link" href={demoUrl} target="_blank">
Демо
<Icon name="link-out" width="15" height="15" />
</a>
)
}
</div>
</div>
<style lang="scss">
.project-item {
display: flex;
flex-direction: column;
border: 1px solid var(--border-color);
border-radius: var(--border-radius-lg);
padding: 15px;
&__heading {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
& h2 {
display: flex;
align-items: center;
font-size: 1.2rem;
font-weight: 600;
column-gap: 10px;
margin: 0;
padding: 0;
}
& span {
font-size: var(--font-size-sm);
color: var(--font-color-gray);
}
}
&__links {
display: flex;
margin-top: auto;
column-gap: 5px;
}
}
</style>

View File

@ -0,0 +1,14 @@
---
---
<div>
<slot />
</div>
<style>
div {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 10px;
}
</style>

View File

@ -1,7 +1,8 @@
export const DEFAULT_LANGUAGE: LanguageKeys = "ru"; export const DEFAULT_LANGUAGE: LanguageKeys = "ru";
/* Show banner about website development */ /* 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 =
import.meta.env.IS_SITE_UNDER_CONSTRUCTION === "true" ? true : false;
export const SUPPORTED_LANGUAGES: SupportedLanguages = ["ru", "en"]; export const SUPPORTED_LANGUAGES: SupportedLanguages = ["ru", "en"];

View File

@ -1,5 +1,6 @@
--- ---
coverImage: /media/serial-port.jpg coverImage: /media/serial-port.jpg
draft: false
author: Sergey Yarkov author: Sergey Yarkov
title: Использование WebSerial API для общения с микроконтроллером title: Использование WebSerial API для общения с микроконтроллером
description: Речь пойдет о том, что такое WebSerial API, как его можно description: Речь пойдет о том, что такое WebSerial API, как его можно
@ -10,9 +11,10 @@ tags:
- AVR - AVR
- WebSerial API - WebSerial API
- Tutorial - Tutorial
pubDate: 2022-11-04 pubDate: 2022-11-04T21:00:00.000Z
draft: false updatedDate: 2023-02-13T16:34:23.326Z
--- ---
# Что вы узнаете # Что вы узнаете
По итогу вы узнаете, как отправлять и принимать данные от устройства с последовательным портом в браузере, разработаете демонстрационное веб-приложение, которое отправляет команды в микроконтроллер с последовательным портом и напишите прошивку для микроконтроллера [AVR128DA48](http://ww1.microchip.com/downloads/en/DeviceDoc/40002183A.pdf) для обработки этих команд. Готовый вариант проекта можно посмотреть [здесь](https://web-serial-example.netlify.app/), исходники загружены в [репозиторий](https://github.com/sergeyyarkov/avr128da48_web-serial-example) на GitHub. По итогу вы узнаете, как отправлять и принимать данные от устройства с последовательным портом в браузере, разработаете демонстрационное веб-приложение, которое отправляет команды в микроконтроллер с последовательным портом и напишите прошивку для микроконтроллера [AVR128DA48](http://ww1.microchip.com/downloads/en/DeviceDoc/40002183A.pdf) для обработки этих команд. Готовый вариант проекта можно посмотреть [здесь](https://web-serial-example.netlify.app/), исходники загружены в [репозиторий](https://github.com/sergeyyarkov/avr128da48_web-serial-example) на GitHub.
@ -47,17 +49,17 @@ WebSerial API - это браузерный API, который предоста
UART передает данные последовательно по одному биту в одном из трех режимов: UART передает данные последовательно по одному биту в одном из трех режимов:
* односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику - односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику
* полудуплексный режим - устройства могут передавать и принимать данные по очереди - полудуплексный режим - устройства могут передавать и принимать данные по очереди
* полнодуплексный режим - устройства могут передавать и принимать данные одновременно - полнодуплексный режим - устройства могут передавать и принимать данные одновременно
Так как в этом проекте предполагается использование асинхронной версии UART, то соответственно необходимо устанавливать **одинаковую** скорость передачи битов в секунду (`baud rate`, `bps`) на двух устройствах. Наиболее распространенные скорости являются: **4800, 9600, 19.2 К, 57.6К и 115.2К.** Так как в этом проекте предполагается использование асинхронной версии UART, то соответственно необходимо устанавливать **одинаковую** скорость передачи битов в секунду (`baud rate`, `bps`) на двух устройствах. Наиболее распространенные скорости являются: **4800, 9600, 19.2 К, 57.6К и 115.2К.**
Данные передаются в виде так называемых `пакетов`, где каждый пакет содержит в себе: Данные передаются в виде так называемых `пакетов`, где каждый пакет содержит в себе:
* стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных. - стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных.
* биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов. - биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов.
* бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок. - бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок.
<figure> <figure>
<img src="/media/uart-packet.jpg" width="700" height="300" alt="Структура UART пакета" format="webp"> <img src="/media/uart-packet.jpg" width="700" height="300" alt="Структура UART пакета" format="webp">
@ -82,7 +84,7 @@ UART передает данные последовательно по одно
Как я упомянул ранее, прошивка будет написана для микроконтроллера AVR128DA48. На самом деле неважно какой микроконтроллер вы используете, вам достаточно нужно уметь отправлять и принимать данные используя UART периферию вашего микроконтроллера. Как я упомянул ранее, прошивка будет написана для микроконтроллера AVR128DA48. На самом деле неважно какой микроконтроллер вы используете, вам достаточно нужно уметь отправлять и принимать данные используя UART периферию вашего микроконтроллера.
Суть программы будет очень проста: необходимо написать некий ***обработчик команд*** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод. Суть программы будет очень проста: необходимо написать некий **_обработчик команд_** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод.
Постановка задачи ясна, теперь рассмотрим реализацию данной программы. Я буду использовать среду разработки MPLAB X IDE и язык C для написания прошивки. Постановка задачи ясна, теперь рассмотрим реализацию данной программы. Я буду использовать среду разработки MPLAB X IDE и язык C для написания прошивки.
@ -102,10 +104,10 @@ UART передает данные последовательно по одно
#endif /* CONSTANTS_H */ #endif /* CONSTANTS_H */
``` ```
* `F_CPU` - тактовая частота микроконтроллера - `F_CPU` - тактовая частота микроконтроллера
* `BAUD_RATE` - скорость передачи данных UART - `BAUD_RATE` - скорость передачи данных UART
* `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные - `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные
* `EOT` - End-of-Transmission, ASCII символ конца передачи данных - `EOT` - End-of-Transmission, ASCII символ конца передачи данных
## Инициализация USART ## Инициализация USART
@ -155,11 +157,11 @@ void USART1_Initialize(void) {
Для полнодуплексного режима инициализация асинхронной версии USART происходит следующим образом: Для полнодуплексного режима инициализация асинхронной версии USART происходит следующим образом:
* Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD` - Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD`
* Конфигурация размера `фрейма`, в нашем случае это 8 бит. - Конфигурация размера `фрейма`, в нашем случае это 8 бит.
* Конфигурация пина `TX` на выход и пина `RX` на вход. - Конфигурация пина `TX` на выход и пина `RX` на вход.
* Включение прерывания на то, когда закончился прием данных (когда пришел один пакет). - Включение прерывания на то, когда закончился прием данных (когда пришел один пакет).
* Включение приемника и передатчика. - Включение приемника и передатчика.
Теперь, рассмотрим функции отправки данных: Теперь, рассмотрим функции отправки данных:
@ -211,9 +213,9 @@ void command_list(void);
Для решения этой задачи я предлагаю создавать массив структур, где в каждой структуре будет содержаться указатель на функцию которую нам нужно выполнить, и название самой команды. Для решения этой задачи я предлагаю создавать массив структур, где в каждой структуре будет содержаться указатель на функцию которую нам нужно выполнить, и название самой команды.
* `command_define` будет создавать новую структуру в массиве. - `command_define` будет создавать новую структуру в массиве.
* `command_process` будет вызывать соответствующую функцию по имени команды. - `command_process` будет вызывать соответствующую функцию по имени команды.
* `command_list` будет отправлять по UART информацию о существующих командах. - `command_list` будет отправлять по UART информацию о существующих командах.
Реализовать задуманное можно следующим образом создав файл `command.c`: Реализовать задуманное можно следующим образом создав файл `command.c`:
@ -367,9 +369,9 @@ void hello_world(void) {
С помощью функции `command_define` мы можем определить, по какой переданной команде по USART запускать функцию: С помощью функции `command_define` мы можем определить, по какой переданной команде по USART запускать функцию:
* `led_toggle` будет переключать светодиод. - `led_toggle` будет переключать светодиод.
* `hello_world` выводить сообщение “Hello world”. - `hello_world` выводить сообщение “Hello world”.
* `command_list` будет показывать список имеющихся команд. - `command_list` будет показывать список имеющихся команд.
## Демонстрация работы ## Демонстрация работы
@ -377,10 +379,10 @@ void hello_world(void) {
Выберете ваше устройство из списка (в моем случае это `/dev/ttyACM1`) и установите следующие настройки: Выберете ваше устройство из списка (в моем случае это `/dev/ttyACM1`) и установите следующие настройки:
* Скорость: **9600** бод - Скорость: **9600** бод
* Количество битов: **8** - Количество битов: **8**
* Четность: **None** - Четность: **None**
* Стоповые биты: **1** - Стоповые биты: **1**
<figure> <figure>
<img <img
@ -584,12 +586,12 @@ class SerialPortHandler {
} }
``` ```
* [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) является классом, который кодирует строку в массив без знаковых 8-ми битных целых чисел [`Uint8Array`](https://webidl.spec.whatwg.org/#idl-Uint8Array). - [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) является классом, который кодирует строку в массив без знаковых 8-ми битных целых чисел [`Uint8Array`](https://webidl.spec.whatwg.org/#idl-Uint8Array).
* [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder#examples) наоборот, декодирует в строку. Экземпляры этих классов нам понадобятся, т.к мы будет оперировать данными с типом `Uint8Array`. - [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder#examples) наоборот, декодирует в строку. Экземпляры этих классов нам понадобятся, т.к мы будет оперировать данными с типом `Uint8Array`.
* Функции `onConnect` и `onDisconnect` будут вызываться при подключении или отключении устройства. - Функции `onConnect` и `onDisconnect` будут вызываться при подключении или отключении устройства.
* Свойство [`options`](https://wicg.github.io/serial/#serialoptions-dictionary) содержит необходимые параметры, которые будут использоваться для передачи и приема данных. - Свойство [`options`](https://wicg.github.io/serial/#serialoptions-dictionary) содержит необходимые параметры, которые будут использоваться для передачи и приема данных.
* Свойство [`port`](https://wicg.github.io/serial/#serialport-interface) - объект, который будет содержать методы для работы с портом и информацию об устройстве после установки соединения. - Свойство [`port`](https://wicg.github.io/serial/#serialport-interface) - объект, который будет содержать методы для работы с портом и информацию об устройстве после установки соединения.
* Функция `setupListeners` просто будет добавлять обработчики событий. - Функция `setupListeners` просто будет добавлять обработчики событий.
Далее рассматривается реализация этих методов. Далее рассматривается реализация этих методов.
@ -638,12 +640,12 @@ const port = await navigator.serial.requestPort({
После того как пользователь выбрал устройство, метод `requestPort` возвращает [`port`](https://wicg.github.io/serial/#serialport-interface), который можно открыть передав туда [параметры](https://wicg.github.io/serial/#serialoptions-dictionary): После того как пользователь выбрал устройство, метод `requestPort` возвращает [`port`](https://wicg.github.io/serial/#serialport-interface), который можно открыть передав туда [параметры](https://wicg.github.io/serial/#serialoptions-dictionary):
* `baudRate` : скорость передачи данных. - `baudRate` : скорость передачи данных.
* `dataBits` : количество бит данных во фрейме (7 или 8). - `dataBits` : количество бит данных во фрейме (7 или 8).
* `stopBits` : количество стоповых битов в конце пакета (1 или 2). - `stopBits` : количество стоповых битов в конце пакета (1 или 2).
* `parity` : режим четности ( `none` , `even` или `odd` ). - `parity` : режим четности ( `none` , `even` или `odd` ).
* `bufferSize` : размер буферов чтения и записи, которые должны быть созданы. - `bufferSize` : размер буферов чтения и записи, которые должны быть созданы.
* `flowControl` : режим управления потоком ( `none` или `hardware` ). - `flowControl` : режим управления потоком ( `none` или `hardware` ).
Свойство `baudRate` является единственным обязательным параметром, остальные же являются необязательными и имеют [значения по умолчанию](https://wicg.github.io/serial/#serialoptions-dictionary). Свойство `baudRate` является единственным обязательным параметром, остальные же являются необязательными и имеют [значения по умолчанию](https://wicg.github.io/serial/#serialoptions-dictionary).
@ -891,8 +893,8 @@ setInterval(async () => {
# Полезные материалы # Полезные материалы
* [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial) - [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial)
* [Документация к WebSerial API](https://wicg.github.io/serial/) - [Документация к WebSerial API](https://wicg.github.io/serial/)
* [Подробное руководство по Stream API](https://web.dev/streams/) - [Подробное руководство по Stream API](https://web.dev/streams/)
* [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors) - [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors)
* [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/) - [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/)

View File

@ -13,8 +13,10 @@ const blog = defineCollection({
.transform((val) => new Date(val)), .transform((val) => new Date(val)),
updatedDate: z updatedDate: z
.string() .string()
.or(z.date())
.optional() .optional()
.transform((str) => (str ? new Date(str) : undefined)), .transform((str) => (str ? new Date(str) : undefined)),
draft: z.boolean(),
}), }),
}); });
@ -31,4 +33,17 @@ const authors = defineCollection({
}), }),
}); });
export const collections = { blog, tags, authors }; const projects = defineCollection({
schema: z.object({
icon: z.string(),
title: z.string(),
year: z.number(),
description: z.string(),
sourceUrl: z.string(),
demoUrl: z.string().nullable().optional(),
articleUrl: z.string().nullable().optional(),
draft: z.boolean(),
}),
});
export const collections = { blog, tags, authors, projects };

View File

@ -0,0 +1,11 @@
---
icon: triangle_filled
draft: false
title: devthread
year: 2021
description: Static website for publishing various articles, developed with
Gatsby.js and NetlifyCMS
sourceUrl: https://github.com/sergeyyarkov/devthread.ru
demoUrl: https://devthread.yarkov.tech
articleUrl: null
---

View File

@ -0,0 +1,10 @@
---
icon: round-shop-two
draft: false
title: digital-store
year: 2019
description: Customizable online store with control panel for selling digital goods
sourceUrl: https://github.com/sergeyyarkov/digital-store/tree/digital-store-docker
demoUrl: https://digital-store.herokuapp.com
articleUrl: null
---

View File

@ -0,0 +1,10 @@
---
icon: round-school
draft: false
title: educt
year: 2022
description: Learning Management System (LMS) to publish your courses, lectures, lessons
sourceUrl: https://github.com/sergeyyarkov/educt
demoUrl: https://educt.yarkov.tech
articleUrl: null
---

View File

@ -0,0 +1,10 @@
---
icon: chip
draft: false
title: memory-led-game
year: 2022
description: Analogue of the popular game "Simon says" on the AVR microcontroller
sourceUrl: https://github.com/sergeyyarkov/attiny24a_memory-led-game
demoUrl: null
articleUrl: null
---

View File

@ -0,0 +1,10 @@
---
icon: nodejs
draft: false
title: node-docker-example
year: 2022
description: An example of using Docker to deploy a Node.js application
sourceUrl: https://github.com/sergeyyarkov/node-docker-example
demoUrl: https://node-docker-example.onrender.com
articleUrl: null
---

View File

@ -0,0 +1,10 @@
---
icon: chat
draft: false
title: react-chat-app
year: 2021
description: Client-server web chat application developed with Node.js and Socket.io
sourceUrl: https://github.com/sergeyyarkov/react-chat-app
demoUrl: null
articleUrl: null
---

View File

@ -0,0 +1,11 @@
---
icon: serial
draft: false
title: web-serial-example
year: 2022
description: An example of using the Web Serial API to interact with the
microcontroller through a browser
sourceUrl: https://github.com/sergeyyarkov/avr128da48_web-serial-example
demoUrl: https://web-serial-example.netlify.app
articleUrl: null
---

View File

@ -0,0 +1,11 @@
---
icon: book
draft: false
title: yarkov.tech
year: 2023
description: My personal website where you can find information about me and a
list of various articles and tutorials...
sourceUrl: https://github.com/sergeyyarkov/yarkov.tech
demoUrl: https://yarkov.tech
articleUrl: null
---

View File

@ -0,0 +1,10 @@
---
icon: triangle_filled
draft: false
title: devthread
year: 2021
description: Статический веб-сайт для публикации различных статей, разработанный
при помощи Gatsby.js и NetlifyCMS
sourceUrl: https://github.com/sergeyyarkov/devthread.ru
demoUrl: https://devthread.yarkov.tech
---

View File

@ -0,0 +1,10 @@
---
icon: round-shop-two
draft: false
title: digital-store
year: 2019
description: Настраиваемый интернет-магазин с панелью управления для продажи
цифровых товаров
sourceUrl: https://github.com/sergeyyarkov/digital-store/tree/digital-store-docker
demoUrl: https://digital-store.herokuapp.com
---

View File

@ -0,0 +1,9 @@
---
icon: round-school
draft: false
title: educt
year: 2022
description: Система управления обучением (LMS) для публикации ваших курсов, лекций, уроков
sourceUrl: https://github.com/sergeyyarkov/educt
demoUrl: https://educt.yarkov.tech
---

View File

@ -0,0 +1,8 @@
---
icon: chip
draft: false
title: memory-led-game
year: 2022
description: Аналог популярной игры "Саймон говорит" на AVR микроконтроллере
sourceUrl: https://github.com/sergeyyarkov/attiny24a_memory-led-game
---

View File

@ -0,0 +1,9 @@
---
icon: nodejs
draft: false
title: node-docker-example
year: 2022
description: Пример использования Docker для развертывания Node.js приложения
sourceUrl: https://github.com/sergeyyarkov/node-docker-example
demoUrl: https://node-docker-example.onrender.com
---

View File

@ -0,0 +1,9 @@
---
icon: chat
draft: false
title: react-chat-app
year: 2021
description: Клиент-серверное веб приложение чата разработанное при помощи
Node.js и Socket.io
sourceUrl: https://github.com/sergeyyarkov/react-chat-app
---

View File

@ -0,0 +1,11 @@
---
icon: serial
draft: false
title: web-serial-example
year: 2022
description: Пример использования Web Serial API для взаимодействия с
микроконтроллером через браузер.
sourceUrl: https://github.com/sergeyyarkov/avr128da48_web-serial-example
demoUrl: https://web-serial-example.netlify.app
articleUrl: https://yarkov.tech/blog/2022-11-04/ispolzovanie-webserial-api-dlya-obshcheniya-s-mikrokontrollerom/
---

View File

@ -0,0 +1,10 @@
---
icon: book
draft: false
title: yarkov.tech
year: 2023
description: Мой персональный веб-сайт где находятся информация обо мне и список
различных статей и туториалов...
sourceUrl: https://github.com/sergeyyarkov/yarkov.tech
demoUrl: https://yarkov.tech
---

View File

@ -0,0 +1,3 @@
globalTitle: yarkov.tech
articles:
recentLimit: 5

View File

@ -1,5 +1,4 @@
export default { export default {
ru: "Русский", ru: "Русский",
en: "English", en: "English",
de: "Deutsch",
}; };

View File

@ -1,6 +1,5 @@
import type { AstroGlobal, MDXInstance } from "astro";
import languages from "./languages"; import languages from "./languages";
import { DEFAULT_LANGUAGE } from "@root/constants"; import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants";
function transformExports<T>(modules: Record<string, { default: T }>) { function transformExports<T>(modules: Record<string, { default: T }>) {
const translations: Record<string, T> = {}; const translations: Record<string, T> = {};
@ -11,6 +10,12 @@ function transformExports<T>(modules: Record<string, { default: T }>) {
return translations; return translations;
} }
export function getLanguageKeys(): Array<string> {
return Object.keys(languages).filter((lang) =>
SUPPORTED_LANGUAGES.includes(lang as LanguageKeys)
);
}
export const getLanguageFromURL = (pathname: string): LanguageKeys => { export const getLanguageFromURL = (pathname: string): LanguageKeys => {
const lang = pathname.split("/")[1] as LanguageKeys; const lang = pathname.split("/")[1] as LanguageKeys;
if (Object.hasOwn(languages, lang)) return lang; if (Object.hasOwn(languages, lang)) return lang;

View File

@ -1,38 +1,42 @@
--- ---
import type { Props as BaseHeadProps } from "@root/components/BaseHead.astro"; import type { CollectionEntry } from "astro:content";
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";
import ArticleContent from "@components/ArticleContent/ArticleContent.astro"; import ArticleContent from "@components/ArticleContent/ArticleContent.astro";
import ArticlePost from "@components/ArticlePost/ArticlePost.astro"; import ArticlePost from "@components/ArticlePost/ArticlePost.astro";
import { SITE_METADATA } from "@root/constants"; import OpenGraph from "@components/OpenGraph.astro";
import type { CollectionEntry } from "astro:content";
export interface Props { export interface Props {
lang?: string | undefined; lang?: string | undefined;
content: CollectionEntry<"blog">; article: CollectionEntry<"blog">;
} }
const { lang = "ru", content } = Astro.props as Props;
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
const { lang = DEFAULT_LANGUAGE, article } = Astro.props as Props;
const {
data: { title, description, coverImage },
} = article;
--- ---
<html lang={lang}> <html lang={lang}>
<BaseHead title={content.data.title} description={content.data.description}> <BaseHead title={title} description={description}>
<meta property="og:title" content={content.data.title} /> <OpenGraph
<meta property="og:site_name" content={SITE_METADATA.name} /> title={title}
<meta property="og:description" content={content.data.description} /> siteName={SITE_METADATA.name}
<meta property="og:type" content="article" /> description={description}
<!-- <meta property="og:url" content={canonicalUrl} /> --> type="article"
<!-- {content.coverImage && ( url={canonicalUrl.href}
<meta property="og:image" content={isURL(content.coverImage) ? content.coverImage : `${Astro.url.origin}${content.coverImage}`} /> image={coverImage
<meta property="og:image:width" content="700" /> ? { path: coverImage, width: "700", height: "400", type: coverImage }
<meta property="og:image:height" content="400" /> : undefined}
<meta property="og:image:type" content={`${mime.lookup(extname(content.coverImage))}`} /> />
)} -->
<script is:inline defer src="/assets/js/load-code-highlight.js"></script> <script is:inline defer src="/assets/js/load-code-highlight.js"></script>
</BaseHead> </BaseHead>
<BaseLayout> <BaseLayout>
<div class="container"> <div class="container">
<ArticlePost lang={lang} article={content}> <ArticlePost lang={lang} article={article}>
<ArticleContent> <ArticleContent>
<slot /> <slot />
</ArticleContent> </ArticleContent>

View File

@ -1,6 +1,6 @@
--- ---
import type { Props as BaseHeadProps } from "@components/BaseHead.astro"; import type { Props as BaseHeadProps } from "@components/BaseHead.astro";
import { 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";
@ -10,11 +10,14 @@ export interface Props extends BaseHeadProps {
canonicalUrl?: string | undefined; canonicalUrl?: string | undefined;
} }
const { title, description, lang = "ru", noIndex = false } = Astro.props as Props; const { title, description, lang = DEFAULT_LANGUAGE, noIndex = false } = Astro.props as Props;
--- ---
<html lang={lang}> <html lang={lang}>
<BaseHead title={title ? `${title} | ${SITE_METADATA.title}` : SITE_METADATA.title} description={description}> <BaseHead
title={title ? `${title} | ${SITE_METADATA.title}` : SITE_METADATA.title}
description={description}
>
{noIndex && <meta name="robots" content="noindex" />} {noIndex && <meta name="robots" content="noindex" />}
</BaseHead> </BaseHead>
<BaseLayout> <BaseLayout>

5
src/pages/404.astro Normal file
View File

@ -0,0 +1,5 @@
---
import NotFound from "@templates/NotFound.astro";
---
<NotFound />

22
src/pages/[...slug].astro Normal file
View File

@ -0,0 +1,22 @@
---
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>

View File

@ -0,0 +1,13 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { DEFAULT_LANGUAGE } from "@root/constants";
import About from "@templates/About.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---
<About />

View File

@ -0,0 +1,13 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { DEFAULT_LANGUAGE } from "@root/constants";
import Articles from "@templates/Articles.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---
<Articles />

View File

@ -0,0 +1,13 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { DEFAULT_LANGUAGE } from "@root/constants";
import Contacts from "@templates/Contacts.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---
<Contacts />

View File

@ -0,0 +1,13 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { DEFAULT_LANGUAGE } from "@root/constants";
import Home from "@templates/Home.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---
<Home />

View File

@ -0,0 +1,13 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { DEFAULT_LANGUAGE } from "@root/constants";
import Projects from "@templates/Projects.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---
<Projects />

5
src/pages/about.astro Normal file
View File

@ -0,0 +1,5 @@
---
import About from "@templates/About.astro";
---
<About />

5
src/pages/articles.astro Normal file
View File

@ -0,0 +1,5 @@
---
import Articles from "@templates/Articles.astro";
---
<Articles />

View File

@ -1,21 +0,0 @@
---
import { CollectionEntry, getCollection } from "astro:content";
import ArticleLayout from "@root/layouts/article.astro";
export async function getStaticPaths() {
const posts = await getCollection("blog");
return posts.map((post) => ({
params: { slug: post.slug },
props: post,
}));
}
type Props = CollectionEntry<"blog">;
const post = Astro.props;
const { Content } = await post.render();
---
<ArticleLayout content={post}>
<Content />
</ArticleLayout>

11
src/pages/ping.astro Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="robots" content="noindex" />
<title>pong</title>
</head>
<body>pong</body>
</html>

5
src/pages/projects.astro Normal file
View File

@ -0,0 +1,5 @@
---
import Projects from "@root/templates/Projects.astro";
---
<Projects />

26
src/templates/About.astro Normal file
View File

@ -0,0 +1,26 @@
---
import PageLayout from "@layouts/default.astro";
import { getLanguageFromURL } from "@i18n/utils";
// import { useTranslation } from "@i18n/utils";
// import * as API from "@root/lib/api";
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);
// const t = useTranslation(Astro);
// const data = await API.fetchAboutMe(Astro);
// const { skills } = SITE_METADATA;
---
<PageLayout noIndex lang={lang} title={"Обо мне"}>
<PageHeading heading={"Обо мне"} />
<section>
<!-- <ArticleContent>
{data && <data.Content />}
</ArticleContent> -->
<!-- <SkillsList>
{skills.map((block) => <SkillsBlock data={block} />)}
</SkillsList> -->
</section>
</PageLayout>

View File

@ -0,0 +1,39 @@
---
import PageLayout from "@layouts/default.astro";
import PageHeading from "@components/PageHeading.astro";
// import ArticlesList from "@components/ArticlesList";
// import * as utils from "@utils/articles";
import { getLanguageFromURL } from "@i18n/utils";
// import { useTranslation } from "@i18n/utils";
// import * as API from "@root/lib/api";
// const t = useTranslation(Astro);
// const data = API.fetchTranslatedArticles(Astro);
// const tags = new Set(data.map((article) => article.frontmatter.tags).flat());
const lang = getLanguageFromURL(Astro.url.pathname);
// const articles = utils.transformArticles(data);
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
---
<PageLayout
lang={lang}
title={"Статьи"}
description={"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь."}
canonicalUrl={canonicalUrl.href}
>
<PageHeading
heading={"Статьи"}
description={"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь."}
/>
<section>
...
<!-- <ArticlesList
client:load
lang={lang}
i18n={{
"input.search": t("input.search"),
"articles.empty": t("articles.empty"),
}}
data={{ articles: utils.formatToArticleBlocks(articles), tags: [...tags] }}
/> -->
</section>
</PageLayout>

View File

@ -7,7 +7,9 @@ import SplashLink from "@root/components/Splash/SplashLink.astro";
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import RecentArticles from "@root/components/RecentArticles.astro"; import RecentArticles from "@root/components/RecentArticles.astro";
const articles = (await getCollection("blog")).sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf()); const articles = (await getCollection("blog"))
.filter((p) => !p.data.draft)
.sort((a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf());
--- ---
<PageLayout description="Home page" lang={"ru"}> <PageLayout description="Home page" lang={"ru"}>
@ -18,8 +20,8 @@ const articles = (await getCollection("blog")).sort((a, b) => a.data.pubDate.val
<h2>Fullstack <b>web-developer</b> & <b>freelancer</b></h2> <h2>Fullstack <b>web-developer</b> & <b>freelancer</b></h2>
</hgroup> </hgroup>
<p> <p>
Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую статьи и туториалы на Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую
разные IT темы. статьи и туториалы на разные IT темы.
</p> </p>
</SplashHeading> </SplashHeading>
<SplashLinks> <SplashLinks>

View File

@ -0,0 +1,13 @@
---
import PageLayout from "@layouts/default.astro";
import { getLanguageFromURL } from "@i18n/utils";
const lang = getLanguageFromURL(Astro.url.pathname);
// const t = useTranslation(Astro);
---
<PageLayout lang={lang}>
<div class="container">
<h1 style="font-size:var(--font-size-5xl);">404</h1>
<p>{"Страница не найдена"}</p>
</div>
</PageLayout>

View File

@ -0,0 +1,31 @@
---
import PageLayout from "@layouts/default.astro";
import ProjectsList from "@components/ProjectsList.astro";
import ProjectItem from "@components/ProjectItem.astro";
import PageHeading from "@components/PageHeading.astro";
import { getLanguageFromURL } from "@i18n/utils";
import { getCollection } from "astro:content";
// import { useTranslation } from "@i18n/utils";
// const t = useTranslation(Astro);
const lang = getLanguageFromURL(Astro.url.pathname);
const projects = (await getCollection("projects")).filter(
(p) => !p.data.draft && p.id.split(".")[1] === lang
);
---
<PageLayout
noIndex
lang={lang}
title={"Проекты"}
description={"Несколько моих основных проектов которые были разработаны и выложены в открытый доступ."}
>
<PageHeading
heading={"Проекты"}
description={"Несколько моих основных проектов которые были разработаны и выложены в открытый доступ."}
/>
<section>
<ProjectsList>
{projects.map((project) => <ProjectItem data={project.data} />)}
</ProjectsList>
</section>
</PageLayout>